ntfs-3g version 2010.3.6
[tomato.git] / release / src / router / ntfs-3g / libntfs-3g / security.c
blob213d88c069a17071a4727982adcd937963d43965
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-2009 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 * Internal read
409 * copied and pasted from ntfs_fuse_read() and made independent
410 * of fuse context
413 static int ntfs_local_read(ntfs_inode *ni,
414 ntfschar *stream_name, int stream_name_len,
415 char *buf, size_t size, off_t offset)
417 ntfs_attr *na = NULL;
418 int res, total = 0;
420 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
421 if (!na) {
422 res = -errno;
423 goto exit;
425 if ((size_t)offset < (size_t)na->data_size) {
426 if (offset + size > (size_t)na->data_size)
427 size = na->data_size - offset;
428 while (size) {
429 res = ntfs_attr_pread(na, offset, size, buf);
430 if ((off_t)res < (off_t)size)
431 ntfs_log_perror("ntfs_attr_pread partial read "
432 "(%lld : %lld <> %d)",
433 (long long)offset,
434 (long long)size, res);
435 if (res <= 0) {
436 res = -errno;
437 goto exit;
439 size -= res;
440 offset += res;
441 total += res;
444 res = total;
445 exit:
446 if (na)
447 ntfs_attr_close(na);
448 return res;
453 * Internal write
454 * copied and pasted from ntfs_fuse_write() and made independent
455 * of fuse context
458 static int ntfs_local_write(ntfs_inode *ni,
459 ntfschar *stream_name, int stream_name_len,
460 char *buf, size_t size, off_t offset)
462 ntfs_attr *na = NULL;
463 int res, total = 0;
465 na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
466 if (!na) {
467 res = -errno;
468 goto exit;
470 while (size) {
471 res = ntfs_attr_pwrite(na, offset, size, buf);
472 if (res < (s64)size)
473 ntfs_log_perror("ntfs_attr_pwrite partial write (%lld: "
474 "%lld <> %d)", (long long)offset,
475 (long long)size, res);
476 if (res <= 0) {
477 res = -errno;
478 goto exit;
480 size -= res;
481 offset += res;
482 total += res;
484 res = total;
485 exit:
486 if (na)
487 ntfs_attr_close(na);
488 return res;
493 * Get the first entry of current index block
494 * cut and pasted form ntfs_ie_get_first() in index.c
497 static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
499 return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
503 * Stuff a 256KB block into $SDS before writing descriptors
504 * into the block.
506 * This prevents $SDS from being automatically declared as sparse
507 * when the second copy of the first security descriptor is written
508 * 256KB further ahead.
510 * Having $SDS declared as a sparse file is not wrong by itself
511 * and chkdsk leaves it as a sparse file. It does however complain
512 * and add a sparse flag (0x0200) into field file_attributes of
513 * STANDARD_INFORMATION of $Secure. This probably means that a
514 * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
515 * files (FILE_ATTR_SPARSE_FILE).
517 * Windows normally does not convert to sparse attribute or sparse
518 * file. Stuffing is just a way to get to the same result.
521 static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
523 int res;
524 int written;
525 unsigned long total;
526 char *stuff;
528 res = 0;
529 total = 0;
530 stuff = (char*)ntfs_malloc(STUFFSZ);
531 if (stuff) {
532 memset(stuff, 0, STUFFSZ);
533 do {
534 written = ntfs_local_write(vol->secure_ni,
535 STREAM_SDS, 4, stuff, STUFFSZ, offs);
536 if (written == STUFFSZ) {
537 total += STUFFSZ;
538 offs += STUFFSZ;
539 } else {
540 errno = ENOSPC;
541 res = -1;
543 } while (!res && (total < ALIGN_SDS_BLOCK));
544 free(stuff);
545 } else {
546 errno = ENOMEM;
547 res = -1;
549 return (res);
553 * Enter a new security descriptor into $Secure (data only)
554 * it has to be written twice with an offset of 256KB
556 * Should only be called by entersecurityattr() to ensure consistency
558 * Returns zero if sucessful
561 static int entersecurity_data(ntfs_volume *vol,
562 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
563 le32 hash, le32 keyid, off_t offs, int gap)
565 int res;
566 int written1;
567 int written2;
568 char *fullattr;
569 int fullsz;
570 SECURITY_DESCRIPTOR_HEADER *phsds;
572 res = -1;
573 fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
574 fullattr = (char*)ntfs_malloc(fullsz);
575 if (fullattr) {
577 * Clear the gap from previous descriptor
578 * this could be useful for appending the second
579 * copy to the end of file. When creating a new
580 * 256K block, the gap is cleared while writing
581 * the first copy
583 if (gap)
584 memset(fullattr,0,gap);
585 memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
586 attr,attrsz);
587 phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
588 phsds->hash = hash;
589 phsds->security_id = keyid;
590 phsds->offset = cpu_to_le64(offs);
591 phsds->length = cpu_to_le32(fullsz - gap);
592 written1 = ntfs_local_write(vol->secure_ni,
593 STREAM_SDS, 4, fullattr, fullsz,
594 offs - gap);
595 written2 = ntfs_local_write(vol->secure_ni,
596 STREAM_SDS, 4, fullattr, fullsz,
597 offs - gap + ALIGN_SDS_BLOCK);
598 if ((written1 == fullsz)
599 && (written2 == written1))
600 res = 0;
601 else
602 errno = ENOSPC;
603 free(fullattr);
604 } else
605 errno = ENOMEM;
606 return (res);
610 * Enter a new security descriptor in $Secure (indexes only)
612 * Should only be called by entersecurityattr() to ensure consistency
614 * Returns zero if sucessful
617 static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
618 le32 hash, le32 keyid, off_t offs)
620 union {
621 struct {
622 le32 dataoffsl;
623 le32 dataoffsh;
624 } parts;
625 le64 all;
626 } realign;
627 int res;
628 ntfs_index_context *xsii;
629 ntfs_index_context *xsdh;
630 struct SII newsii;
631 struct SDH newsdh;
633 res = -1;
634 /* enter a new $SII record */
636 xsii = vol->secure_xsii;
637 ntfs_index_ctx_reinit(xsii);
638 newsii.offs = const_cpu_to_le16(20);
639 newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
640 newsii.fill1 = const_cpu_to_le32(0);
641 newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
642 newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
643 newsii.flags = const_cpu_to_le16(0);
644 newsii.fill2 = const_cpu_to_le16(0);
645 newsii.keysecurid = keyid;
646 newsii.hash = hash;
647 newsii.securid = keyid;
648 realign.all = cpu_to_le64(offs);
649 newsii.dataoffsh = realign.parts.dataoffsh;
650 newsii.dataoffsl = realign.parts.dataoffsl;
651 newsii.datasize = cpu_to_le32(attrsz
652 + sizeof(SECURITY_DESCRIPTOR_HEADER));
653 if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
655 /* enter a new $SDH record */
657 xsdh = vol->secure_xsdh;
658 ntfs_index_ctx_reinit(xsdh);
659 newsdh.offs = const_cpu_to_le16(24);
660 newsdh.size = const_cpu_to_le16(
661 sizeof(SECURITY_DESCRIPTOR_HEADER));
662 newsdh.fill1 = const_cpu_to_le32(0);
663 newsdh.indexsz = const_cpu_to_le16(
664 sizeof(struct SDH));
665 newsdh.indexksz = const_cpu_to_le16(
666 sizeof(SDH_INDEX_KEY));
667 newsdh.flags = const_cpu_to_le16(0);
668 newsdh.fill2 = const_cpu_to_le16(0);
669 newsdh.keyhash = hash;
670 newsdh.keysecurid = keyid;
671 newsdh.hash = hash;
672 newsdh.securid = keyid;
673 newsdh.dataoffsh = realign.parts.dataoffsh;
674 newsdh.dataoffsl = realign.parts.dataoffsl;
675 newsdh.datasize = cpu_to_le32(attrsz
676 + sizeof(SECURITY_DESCRIPTOR_HEADER));
677 /* special filler value, Windows generally */
678 /* fills with 0x00490049, sometimes with zero */
679 newsdh.fill3 = const_cpu_to_le32(0x00490049);
680 if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
681 res = 0;
683 return (res);
687 * Enter a new security descriptor in $Secure (data and indexes)
688 * Returns id of entry, or zero if there is a problem.
689 * (should not be called for NTFS version < 3.0)
691 * important : calls have to be serialized, however no locking is
692 * needed while fuse is not multithreaded
695 static le32 entersecurityattr(ntfs_volume *vol,
696 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
697 le32 hash)
699 union {
700 struct {
701 le32 dataoffsl;
702 le32 dataoffsh;
703 } parts;
704 le64 all;
705 } realign;
706 le32 securid;
707 le32 keyid;
708 u32 newkey;
709 off_t offs;
710 int gap;
711 int size;
712 BOOL found;
713 struct SII *psii;
714 INDEX_ENTRY *entry;
715 INDEX_ENTRY *next;
716 ntfs_index_context *xsii;
717 ntfs_attr *na;
718 int olderrno;
720 /* find the first available securid beyond the last key */
721 /* in $Secure:$SII. This also determines the first */
722 /* available location in $Secure:$SDS, as this stream */
723 /* is always appended to and the id's are allocated */
724 /* in sequence */
726 securid = const_cpu_to_le32(0);
727 xsii = vol->secure_xsii;
728 ntfs_index_ctx_reinit(xsii);
729 offs = size = 0;
730 keyid = const_cpu_to_le32(-1);
731 olderrno = errno;
732 found = !ntfs_index_lookup((char*)&keyid,
733 sizeof(SII_INDEX_KEY), xsii);
734 if (!found && (errno != ENOENT)) {
735 ntfs_log_perror("Inconsistency in index $SII");
736 psii = (struct SII*)NULL;
737 } else {
738 /* restore errno to avoid misinterpretation */
739 errno = olderrno;
740 entry = xsii->entry;
741 psii = (struct SII*)xsii->entry;
743 if (psii) {
745 * Get last entry in block, but must get first one
746 * one first, as we should already be beyond the
747 * last one. For some reason the search for the last
748 * entry sometimes does not return the last block...
749 * we assume this can only happen in root block
751 if (xsii->is_in_root)
752 entry = ntfs_ie_get_first
753 ((INDEX_HEADER*)&xsii->ir->index);
754 else
755 entry = ntfs_ie_get_first
756 ((INDEX_HEADER*)&xsii->ib->index);
758 * All index blocks should be at least half full
759 * so there always is a last entry but one,
760 * except when creating the first entry in index root.
761 * A simplified version of next(), limited to
762 * current index node, could be used
764 keyid = const_cpu_to_le32(0);
765 while (entry) {
766 next = ntfs_index_next(entry,xsii);
767 if (next) {
768 psii = (struct SII*)next;
769 /* save last key and */
770 /* available position */
771 keyid = psii->keysecurid;
772 realign.parts.dataoffsh
773 = psii->dataoffsh;
774 realign.parts.dataoffsl
775 = psii->dataoffsl;
776 offs = le64_to_cpu(realign.all);
777 size = le32_to_cpu(psii->datasize);
779 entry = next;
782 if (!keyid) {
784 * could not find any entry, before creating the first
785 * entry, make a double check by making sure size of $SII
786 * is less than needed for one entry
788 securid = const_cpu_to_le32(0);
789 na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
790 if (na) {
791 if ((size_t)na->data_size < sizeof(struct SII)) {
792 ntfs_log_error("Creating the first security_id\n");
793 securid = const_cpu_to_le32(FIRST_SECURITY_ID);
795 ntfs_attr_close(na);
797 if (!securid) {
798 ntfs_log_error("Error creating a security_id\n");
799 errno = EIO;
801 } else {
802 newkey = le32_to_cpu(keyid) + 1;
803 securid = cpu_to_le32(newkey);
806 * The security attr has to be written twice 256KB
807 * apart. This implies that offsets like
808 * 0x40000*odd_integer must be left available for
809 * the second copy. So align to next block when
810 * the last byte overflows on a wrong block.
813 if (securid) {
814 gap = (-size) & (ALIGN_SDS_ENTRY - 1);
815 offs += gap + size;
816 if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
817 & ALIGN_SDS_BLOCK) {
818 offs = ((offs + attrsz
819 + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
820 | (ALIGN_SDS_BLOCK - 1)) + 1;
822 if (!(offs & (ALIGN_SDS_BLOCK - 1)))
823 entersecurity_stuff(vol, offs);
825 * now write the security attr to storage :
826 * first data, then SII, then SDH
827 * If failure occurs while writing SDS, data will never
828 * be accessed through indexes, and will be overwritten
829 * by the next allocated descriptor
830 * If failure occurs while writing SII, the id has not
831 * recorded and will be reallocated later
832 * If failure occurs while writing SDH, the space allocated
833 * in SDS or SII will not be reused, an inconsistency
834 * will persist with no significant consequence
836 if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
837 || entersecurity_indexes(vol, attrsz, hash, securid, offs))
838 securid = const_cpu_to_le32(0);
840 /* inode now is dirty, synchronize it all */
841 ntfs_index_entry_mark_dirty(vol->secure_xsii);
842 ntfs_index_ctx_reinit(vol->secure_xsii);
843 ntfs_index_entry_mark_dirty(vol->secure_xsdh);
844 ntfs_index_ctx_reinit(vol->secure_xsdh);
845 NInoSetDirty(vol->secure_ni);
846 if (ntfs_inode_sync(vol->secure_ni))
847 ntfs_log_perror("Could not sync $Secure\n");
848 return (securid);
852 * Find a matching security descriptor in $Secure,
853 * if none, allocate a new id and write the descriptor to storage
854 * Returns id of entry, or zero if there is a problem.
856 * important : calls have to be serialized, however no locking is
857 * needed while fuse is not multithreaded
860 static le32 setsecurityattr(ntfs_volume *vol,
861 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
863 struct SDH *psdh; /* this is an image of index (le) */
864 union {
865 struct {
866 le32 dataoffsl;
867 le32 dataoffsh;
868 } parts;
869 le64 all;
870 } realign;
871 BOOL found;
872 BOOL collision;
873 size_t size;
874 size_t rdsize;
875 s64 offs;
876 int res;
877 ntfs_index_context *xsdh;
878 char *oldattr;
879 SDH_INDEX_KEY key;
880 INDEX_ENTRY *entry;
881 le32 securid;
882 le32 hash;
883 int olderrno;
885 hash = ntfs_security_hash(attr,attrsz);
886 oldattr = (char*)NULL;
887 securid = const_cpu_to_le32(0);
888 res = 0;
889 xsdh = vol->secure_xsdh;
890 if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
891 ntfs_index_ctx_reinit(xsdh);
893 * find the nearest key as (hash,0)
894 * (do not search for partial key : in case of collision,
895 * it could return a key which is not the first one which
896 * collides)
898 key.hash = hash;
899 key.security_id = const_cpu_to_le32(0);
900 olderrno = errno;
901 found = !ntfs_index_lookup((char*)&key,
902 sizeof(SDH_INDEX_KEY), xsdh);
903 if (!found && (errno != ENOENT))
904 ntfs_log_perror("Inconsistency in index $SDH");
905 else {
906 /* restore errno to avoid misinterpretation */
907 errno = olderrno;
908 entry = xsdh->entry;
909 found = FALSE;
911 * lookup() may return a node with no data,
912 * if so get next
914 if (entry->ie_flags & INDEX_ENTRY_END)
915 entry = ntfs_index_next(entry,xsdh);
916 do {
917 collision = FALSE;
918 psdh = (struct SDH*)entry;
919 if (psdh)
920 size = (size_t) le32_to_cpu(psdh->datasize)
921 - sizeof(SECURITY_DESCRIPTOR_HEADER);
922 else size = 0;
923 /* if hash is not the same, the key is not present */
924 if (psdh && (size > 0)
925 && (psdh->keyhash == hash)) {
926 /* if hash is the same */
927 /* check the whole record */
928 realign.parts.dataoffsh = psdh->dataoffsh;
929 realign.parts.dataoffsl = psdh->dataoffsl;
930 offs = le64_to_cpu(realign.all)
931 + sizeof(SECURITY_DESCRIPTOR_HEADER);
932 oldattr = (char*)ntfs_malloc(size);
933 if (oldattr) {
934 rdsize = ntfs_local_read(
935 vol->secure_ni,
936 STREAM_SDS, 4,
937 oldattr, size, offs);
938 found = (rdsize == size)
939 && !memcmp(oldattr,attr,size);
940 free(oldattr);
941 /* if the records do not compare */
942 /* (hash collision), try next one */
943 if (!found) {
944 entry = ntfs_index_next(
945 entry,xsdh);
946 collision = TRUE;
948 } else
949 res = ENOMEM;
951 } while (collision && entry);
952 if (found)
953 securid = psdh->keysecurid;
954 else {
955 if (res) {
956 errno = res;
957 securid = const_cpu_to_le32(0);
958 } else {
960 * no matching key :
961 * have to build a new one
963 securid = entersecurityattr(vol,
964 attr, attrsz, hash);
969 if (--vol->secure_reentry)
970 ntfs_log_perror("Reentry error, check no multithreading\n");
971 return (securid);
976 * Update the security descriptor of a file
977 * Either as an attribute (complying with pre v3.x NTFS version)
978 * or, when possible, as an entry in $Secure (for NTFS v3.x)
980 * returns 0 if success
983 static int update_secur_descr(ntfs_volume *vol,
984 char *newattr, ntfs_inode *ni)
986 int newattrsz;
987 int written;
988 int res;
989 ntfs_attr *na;
991 newattrsz = ntfs_attr_size(newattr);
993 #if !FORCE_FORMAT_v1x
994 if ((vol->major_ver < 3) || !vol->secure_ni) {
995 #endif
997 /* update for NTFS format v1.x */
999 /* update the old security attribute */
1000 na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
1001 if (na) {
1002 /* resize attribute */
1003 res = ntfs_attr_truncate(na, (s64) newattrsz);
1004 /* overwrite value */
1005 if (!res) {
1006 written = (int)ntfs_attr_pwrite(na, (s64) 0,
1007 (s64) newattrsz, newattr);
1008 if (written != newattrsz) {
1009 ntfs_log_error("Failed to update "
1010 "a v1.x security descriptor\n");
1011 errno = EIO;
1012 res = -1;
1016 ntfs_attr_close(na);
1017 /* if old security attribute was found, also */
1018 /* truncate standard information attribute to v1.x */
1019 /* this is needed when security data is wanted */
1020 /* as v1.x though volume is formatted for v3.x */
1021 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1022 AT_UNNAMED, 0);
1023 if (na) {
1024 clear_nino_flag(ni, v3_Extensions);
1026 * Truncating the record does not sweep extensions
1027 * from copy in memory. Clear security_id to be safe
1029 ni->security_id = const_cpu_to_le32(0);
1030 res = ntfs_attr_truncate(na, (s64)48);
1031 ntfs_attr_close(na);
1032 clear_nino_flag(ni, v3_Extensions);
1034 } else {
1036 * insert the new security attribute if there
1037 * were none
1039 res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
1040 AT_UNNAMED, 0, (u8*)newattr,
1041 (s64) newattrsz);
1043 #if !FORCE_FORMAT_v1x
1044 } else {
1046 /* update for NTFS format v3.x */
1048 le32 securid;
1050 securid = setsecurityattr(vol,
1051 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
1052 (s64)newattrsz);
1053 if (securid) {
1054 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1055 AT_UNNAMED, 0);
1056 if (na) {
1057 res = 0;
1058 if (!test_nino_flag(ni, v3_Extensions)) {
1059 /* expand standard information attribute to v3.x */
1060 res = ntfs_attr_truncate(na,
1061 (s64)sizeof(STANDARD_INFORMATION));
1062 ni->owner_id = const_cpu_to_le32(0);
1063 ni->quota_charged = const_cpu_to_le64(0);
1064 ni->usn = const_cpu_to_le64(0);
1065 ntfs_attr_remove(ni,
1066 AT_SECURITY_DESCRIPTOR,
1067 AT_UNNAMED, 0);
1069 set_nino_flag(ni, v3_Extensions);
1070 ni->security_id = securid;
1071 ntfs_attr_close(na);
1072 } else {
1073 ntfs_log_error("Failed to update "
1074 "standard informations\n");
1075 errno = EIO;
1076 res = -1;
1078 } else
1079 res = -1;
1081 #endif
1083 /* mark node as dirty */
1084 NInoSetDirty(ni);
1085 ntfs_inode_sync(ni); /* useful ? */
1086 return (res);
1090 * Upgrade the security descriptor of a file
1091 * This is intended to allow graceful upgrades for files which
1092 * were created in previous versions, with a security attributes
1093 * and no security id.
1095 * It will allocate a security id and replace the individual
1096 * security attribute by a reference to the global one
1098 * Special files are not upgraded (currently / and files in
1099 * directories /$*)
1101 * Though most code is similar to update_secur_desc() it has
1102 * been kept apart to facilitate the further processing of
1103 * special cases or even to remove it if found dangerous.
1105 * returns 0 if success,
1106 * 1 if not upgradable. This is not an error.
1107 * -1 if there is a problem
1110 static int upgrade_secur_desc(ntfs_volume *vol,
1111 const char *attr, ntfs_inode *ni)
1113 int attrsz;
1114 int res;
1115 le32 securid;
1116 ntfs_attr *na;
1119 * upgrade requires NTFS format v3.x
1120 * also refuse upgrading for special files
1121 * whose number is less than FILE_first_user
1124 if ((vol->major_ver >= 3)
1125 && (ni->mft_no < FILE_first_user)) {
1126 attrsz = ntfs_attr_size(attr);
1127 securid = setsecurityattr(vol,
1128 (const SECURITY_DESCRIPTOR_RELATIVE*)attr,
1129 (s64)attrsz);
1130 if (securid) {
1131 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1132 AT_UNNAMED, 0);
1133 if (na) {
1134 res = 0;
1135 /* expand standard information attribute to v3.x */
1136 res = ntfs_attr_truncate(na,
1137 (s64)sizeof(STANDARD_INFORMATION));
1138 ni->owner_id = const_cpu_to_le32(0);
1139 ni->quota_charged = const_cpu_to_le64(0);
1140 ni->usn = const_cpu_to_le64(0);
1141 ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
1142 AT_UNNAMED, 0);
1143 set_nino_flag(ni, v3_Extensions);
1144 ni->security_id = securid;
1145 ntfs_attr_close(na);
1146 } else {
1147 ntfs_log_error("Failed to upgrade "
1148 "standard informations\n");
1149 errno = EIO;
1150 res = -1;
1152 } else
1153 res = -1;
1154 /* mark node as dirty */
1155 NInoSetDirty(ni);
1156 ntfs_inode_sync(ni); /* useful ? */
1157 } else
1158 res = 1;
1160 return (res);
1164 * Optional simplified checking of group membership
1166 * This only takes into account the groups defined in
1167 * /etc/group at initialization time.
1168 * It does not take into account the groups dynamically set by
1169 * setgroups() nor the changes in /etc/group since initialization
1171 * This optional method could be useful if standard checking
1172 * leads to a performance concern.
1174 * Should not be called for user root, however the group may be root
1178 static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1180 BOOL ingroup;
1181 int grcnt;
1182 gid_t *groups;
1183 struct MAPPING *user;
1185 ingroup = FALSE;
1186 if (uid) {
1187 user = scx->mapping[MAPUSERS];
1188 while (user && ((uid_t)user->xid != uid))
1189 user = user->next;
1190 if (user) {
1191 groups = user->groups;
1192 grcnt = user->grcnt;
1193 while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
1194 ingroup = (grcnt >= 0);
1197 return (ingroup);
1202 * Check whether current thread owner is member of file group
1204 * Should not be called for user root, however the group may be root
1206 * As indicated by Miklos Szeredi :
1208 * The group list is available in
1210 * /proc/$PID/task/$TID/status
1212 * and fuse supplies TID in get_fuse_context()->pid. The only problem is
1213 * finding out PID, for which I have no good solution, except to iterate
1214 * through all processes. This is rather slow, but may be speeded up
1215 * with caching and heuristics (for single threaded programs PID = TID).
1217 * The following implementation gets the group list from
1218 * /proc/$TID/task/$TID/status which apparently exists and
1219 * contains the same data.
1222 static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1224 static char key[] = "\nGroups:";
1225 char buf[BUFSZ+1];
1226 char filename[64];
1227 enum { INKEY, INSEP, INNUM, INEND } state;
1228 int fd;
1229 char c;
1230 int matched;
1231 BOOL ismember;
1232 int got;
1233 char *p;
1234 gid_t grp;
1235 pid_t tid;
1237 if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
1238 ismember = staticgroupmember(scx, uid, gid);
1239 else {
1240 ismember = FALSE; /* default return */
1241 tid = scx->tid;
1242 sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
1243 fd = open(filename,O_RDONLY);
1244 if (fd >= 0) {
1245 got = read(fd, buf, BUFSZ);
1246 buf[got] = 0;
1247 state = INKEY;
1248 matched = 0;
1249 p = buf;
1250 grp = 0;
1252 * A simple automaton to process lines like
1253 * Groups: 14 500 513
1255 do {
1256 c = *p++;
1257 if (!c) {
1258 /* refill buffer */
1259 got = read(fd, buf, BUFSZ);
1260 buf[got] = 0;
1261 p = buf;
1262 c = *p++; /* 0 at end of file */
1264 switch (state) {
1265 case INKEY :
1266 if (key[matched] == c) {
1267 if (!key[++matched])
1268 state = INSEP;
1269 } else
1270 if (key[0] == c)
1271 matched = 1;
1272 else
1273 matched = 0;
1274 break;
1275 case INSEP :
1276 if ((c >= '0') && (c <= '9')) {
1277 grp = c - '0';
1278 state = INNUM;
1279 } else
1280 if ((c != ' ') && (c != '\t'))
1281 state = INEND;
1282 break;
1283 case INNUM :
1284 if ((c >= '0') && (c <= '9'))
1285 grp = grp*10 + c - '0';
1286 else {
1287 ismember = (grp == gid);
1288 if ((c != ' ') && (c != '\t'))
1289 state = INEND;
1290 else
1291 state = INSEP;
1293 default :
1294 break;
1296 } while (!ismember && c && (state != INEND));
1297 close(fd);
1298 if (!c)
1299 ntfs_log_error("No group record found in %s\n",filename);
1300 } else
1301 ntfs_log_error("Could not open %s\n",filename);
1303 return (ismember);
1307 * Cacheing is done two-way :
1308 * - from uid, gid and perm to securid (CACHED_SECURID)
1309 * - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
1311 * CACHED_SECURID data is kept in a most-recent-first list
1312 * which should not be too long to be efficient. Its optimal
1313 * size is depends on usage and is hard to determine.
1315 * CACHED_PERMISSIONS data is kept in a two-level indexed array. It
1316 * is optimal at the expense of storage. Use of a most-recent-first
1317 * list would save memory and provide similar performances for
1318 * standard usage, but not for file servers with too many file
1319 * owners
1321 * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
1322 * for legacy directories which were not allocated a security_id
1323 * it is organized in a most-recent-first list.
1325 * In main caches, data is never invalidated, as the meaning of
1326 * a security_id only changes when user mapping is changed, which
1327 * current implies remounting. However returned entries may be
1328 * overwritten at next update, so data has to be copied elsewhere
1329 * before another cache update is made.
1330 * In legacy cache, data has to be invalidated when protection is
1331 * changed.
1333 * Though the same data may be found in both list, they
1334 * must be kept separately : the interpretation of ACL
1335 * in both direction are approximations which could be non
1336 * reciprocal for some configuration of the user mapping data
1338 * During the process of recompiling ntfs-3g from a tgz archive,
1339 * security processing added 7.6% to the cpu time used by ntfs-3g
1340 * and 30% if the cache is disabled.
1343 static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
1344 u32 securindex)
1346 struct PERMISSIONS_CACHE *cache;
1347 unsigned int index1;
1348 unsigned int i;
1350 cache = (struct PERMISSIONS_CACHE*)NULL;
1351 /* create the first permissions blocks */
1352 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1353 cache = (struct PERMISSIONS_CACHE*)
1354 ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
1355 + index1*sizeof(struct CACHED_PERMISSIONS*));
1356 if (cache) {
1357 cache->head.last = index1;
1358 cache->head.p_reads = 0;
1359 cache->head.p_hits = 0;
1360 cache->head.p_writes = 0;
1361 *scx->pseccache = cache;
1362 for (i=0; i<=index1; i++)
1363 cache->cachetable[i]
1364 = (struct CACHED_PERMISSIONS*)NULL;
1366 return (cache);
1370 * Free memory used by caches
1371 * The only purpose is to facilitate the detection of memory leaks
1374 static void free_caches(struct SECURITY_CONTEXT *scx)
1376 unsigned int index1;
1377 struct PERMISSIONS_CACHE *pseccache;
1379 pseccache = *scx->pseccache;
1380 if (pseccache) {
1381 for (index1=0; index1<=pseccache->head.last; index1++)
1382 if (pseccache->cachetable[index1]) {
1383 #if POSIXACLS
1384 struct CACHED_PERMISSIONS *cacheentry;
1385 unsigned int index2;
1387 for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
1388 cacheentry = &pseccache->cachetable[index1][index2];
1389 if (cacheentry->valid
1390 && cacheentry->pxdesc)
1391 free(cacheentry->pxdesc);
1393 #endif
1394 free(pseccache->cachetable[index1]);
1396 free(pseccache);
1400 static int compare(const struct CACHED_SECURID *cached,
1401 const struct CACHED_SECURID *item)
1403 #if POSIXACLS
1404 size_t csize;
1405 size_t isize;
1407 /* only compare data and sizes */
1408 csize = (cached->variable ?
1409 sizeof(struct POSIX_ACL)
1410 + (((struct POSIX_SECURITY*)cached->variable)->acccnt
1411 + ((struct POSIX_SECURITY*)cached->variable)->defcnt)
1412 *sizeof(struct POSIX_ACE) :
1414 isize = (item->variable ?
1415 sizeof(struct POSIX_ACL)
1416 + (((struct POSIX_SECURITY*)item->variable)->acccnt
1417 + ((struct POSIX_SECURITY*)item->variable)->defcnt)
1418 *sizeof(struct POSIX_ACE) :
1420 return ((cached->uid != item->uid)
1421 || (cached->gid != item->gid)
1422 || (cached->dmode != item->dmode)
1423 || (csize != isize)
1424 || (csize
1425 && isize
1426 && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
1427 &((struct POSIX_SECURITY*)item->variable)->acl, csize)));
1428 #else
1429 return ((cached->uid != item->uid)
1430 || (cached->gid != item->gid)
1431 || (cached->dmode != item->dmode));
1432 #endif
1435 static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
1436 const struct CACHED_PERMISSIONS_LEGACY *item)
1438 return (cached->mft_no != item->mft_no);
1442 * Resize permission cache table
1443 * do not call unless resizing is needed
1445 * If allocation fails, the cache size is not updated
1446 * Lack of memory is not considered as an error, the cache is left
1447 * consistent and errno is not set.
1450 static void resize_cache(struct SECURITY_CONTEXT *scx,
1451 u32 securindex)
1453 struct PERMISSIONS_CACHE *oldcache;
1454 struct PERMISSIONS_CACHE *newcache;
1455 int newcnt;
1456 int oldcnt;
1457 unsigned int index1;
1458 unsigned int i;
1460 oldcache = *scx->pseccache;
1461 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1462 newcnt = index1 + 1;
1463 if (newcnt <= ((CACHE_PERMISSIONS_SIZE
1464 + (1 << CACHE_PERMISSIONS_BITS)
1465 - 1) >> CACHE_PERMISSIONS_BITS)) {
1466 /* expand cache beyond current end, do not use realloc() */
1467 /* to avoid losing data when there is no more memory */
1468 oldcnt = oldcache->head.last + 1;
1469 newcache = (struct PERMISSIONS_CACHE*)
1470 ntfs_malloc(
1471 sizeof(struct PERMISSIONS_CACHE)
1472 + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1473 if (newcache) {
1474 memcpy(newcache,oldcache,
1475 sizeof(struct PERMISSIONS_CACHE)
1476 + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1477 free(oldcache);
1478 /* mark new entries as not valid */
1479 for (i=newcache->head.last+1; i<=index1; i++)
1480 newcache->cachetable[i]
1481 = (struct CACHED_PERMISSIONS*)NULL;
1482 newcache->head.last = index1;
1483 *scx->pseccache = newcache;
1489 * Enter uid, gid and mode into cache, if possible
1491 * returns the updated or created cache entry,
1492 * or NULL if not possible (typically if there is no
1493 * security id associated)
1496 #if POSIXACLS
1497 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1498 ntfs_inode *ni, uid_t uid, gid_t gid,
1499 struct POSIX_SECURITY *pxdesc)
1500 #else
1501 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1502 ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
1503 #endif
1505 struct CACHED_PERMISSIONS *cacheentry;
1506 struct CACHED_PERMISSIONS *cacheblock;
1507 struct PERMISSIONS_CACHE *pcache;
1508 u32 securindex;
1509 #if POSIXACLS
1510 int pxsize;
1511 struct POSIX_SECURITY *pxcached;
1512 #endif
1513 unsigned int index1;
1514 unsigned int index2;
1515 int i;
1517 /* cacheing is only possible if a security_id has been defined */
1518 if (test_nino_flag(ni, v3_Extensions)
1519 && ni->security_id) {
1521 * Immediately test the most frequent situation
1522 * where the entry exists
1524 securindex = le32_to_cpu(ni->security_id);
1525 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1526 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1527 pcache = *scx->pseccache;
1528 if (pcache
1529 && (pcache->head.last >= index1)
1530 && pcache->cachetable[index1]) {
1531 cacheentry = &pcache->cachetable[index1][index2];
1532 cacheentry->uid = uid;
1533 cacheentry->gid = gid;
1534 #if POSIXACLS
1535 if (cacheentry->valid && cacheentry->pxdesc)
1536 free(cacheentry->pxdesc);
1537 if (pxdesc) {
1538 pxsize = sizeof(struct POSIX_SECURITY)
1539 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1540 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1541 if (pxcached) {
1542 memcpy(pxcached, pxdesc, pxsize);
1543 cacheentry->pxdesc = pxcached;
1544 } else {
1545 cacheentry->valid = 0;
1546 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1548 cacheentry->mode = pxdesc->mode & 07777;
1549 } else
1550 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1551 #else
1552 cacheentry->mode = mode & 07777;
1553 #endif
1554 cacheentry->inh_fileid = const_cpu_to_le32(0);
1555 cacheentry->inh_dirid = const_cpu_to_le32(0);
1556 cacheentry->valid = 1;
1557 pcache->head.p_writes++;
1558 } else {
1559 if (!pcache) {
1560 /* create the first cache block */
1561 pcache = create_caches(scx, securindex);
1562 } else {
1563 if (index1 > pcache->head.last) {
1564 resize_cache(scx, securindex);
1565 pcache = *scx->pseccache;
1568 /* allocate block, if cache table was allocated */
1569 if (pcache && (index1 <= pcache->head.last)) {
1570 cacheblock = (struct CACHED_PERMISSIONS*)
1571 malloc(sizeof(struct CACHED_PERMISSIONS)
1572 << CACHE_PERMISSIONS_BITS);
1573 pcache->cachetable[index1] = cacheblock;
1574 for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
1575 cacheblock[i].valid = 0;
1576 cacheentry = &cacheblock[index2];
1577 if (cacheentry) {
1578 cacheentry->uid = uid;
1579 cacheentry->gid = gid;
1580 #if POSIXACLS
1581 if (pxdesc) {
1582 pxsize = sizeof(struct POSIX_SECURITY)
1583 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1584 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1585 if (pxcached) {
1586 memcpy(pxcached, pxdesc, pxsize);
1587 cacheentry->pxdesc = pxcached;
1588 } else {
1589 cacheentry->valid = 0;
1590 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1592 cacheentry->mode = pxdesc->mode & 07777;
1593 } else
1594 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1595 #else
1596 cacheentry->mode = mode & 07777;
1597 #endif
1598 cacheentry->inh_fileid = const_cpu_to_le32(0);
1599 cacheentry->inh_dirid = const_cpu_to_le32(0);
1600 cacheentry->valid = 1;
1601 pcache->head.p_writes++;
1603 } else
1604 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1606 } else {
1607 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1608 #if CACHE_LEGACY_SIZE
1609 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1610 struct CACHED_PERMISSIONS_LEGACY wanted;
1611 struct CACHED_PERMISSIONS_LEGACY *legacy;
1613 wanted.perm.uid = uid;
1614 wanted.perm.gid = gid;
1615 #if POSIXACLS
1616 wanted.perm.mode = pxdesc->mode & 07777;
1617 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1618 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1619 wanted.mft_no = ni->mft_no;
1620 wanted.variable = (void*)pxdesc;
1621 wanted.varsize = sizeof(struct POSIX_SECURITY)
1622 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1623 #else
1624 wanted.perm.mode = mode & 07777;
1625 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1626 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1627 wanted.mft_no = ni->mft_no;
1628 wanted.variable = (void*)NULL;
1629 wanted.varsize = 0;
1630 #endif
1631 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
1632 scx->vol->legacy_cache, GENERIC(&wanted),
1633 (cache_compare)leg_compare);
1634 if (legacy) {
1635 cacheentry = &legacy->perm;
1636 #if POSIXACLS
1638 * give direct access to the cached pxdesc
1639 * in the permissions structure
1641 cacheentry->pxdesc = legacy->variable;
1642 #endif
1645 #endif
1647 return (cacheentry);
1651 * Fetch owner, group and permission of a file, if cached
1653 * Beware : do not use the returned entry after a cache update :
1654 * the cache may be relocated making the returned entry meaningless
1656 * returns the cache entry, or NULL if not available
1659 static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
1660 ntfs_inode *ni)
1662 struct CACHED_PERMISSIONS *cacheentry;
1663 struct PERMISSIONS_CACHE *pcache;
1664 u32 securindex;
1665 unsigned int index1;
1666 unsigned int index2;
1668 /* cacheing is only possible if a security_id has been defined */
1669 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1670 if (test_nino_flag(ni, v3_Extensions)
1671 && (ni->security_id)) {
1672 securindex = le32_to_cpu(ni->security_id);
1673 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1674 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1675 pcache = *scx->pseccache;
1676 if (pcache
1677 && (pcache->head.last >= index1)
1678 && pcache->cachetable[index1]) {
1679 cacheentry = &pcache->cachetable[index1][index2];
1680 /* reject if entry is not valid */
1681 if (!cacheentry->valid)
1682 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1683 else
1684 pcache->head.p_hits++;
1685 if (pcache)
1686 pcache->head.p_reads++;
1689 #if CACHE_LEGACY_SIZE
1690 else {
1691 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1692 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1693 struct CACHED_PERMISSIONS_LEGACY wanted;
1694 struct CACHED_PERMISSIONS_LEGACY *legacy;
1696 wanted.mft_no = ni->mft_no;
1697 wanted.variable = (void*)NULL;
1698 wanted.varsize = 0;
1699 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
1700 scx->vol->legacy_cache, GENERIC(&wanted),
1701 (cache_compare)leg_compare);
1702 if (legacy) cacheentry = &legacy->perm;
1705 #endif
1706 #if POSIXACLS
1707 if (cacheentry && !cacheentry->pxdesc) {
1708 ntfs_log_error("No Posix descriptor in cache\n");
1709 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1711 #endif
1712 return (cacheentry);
1716 * Retrieve a security attribute from $Secure
1719 static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
1721 struct SII *psii;
1722 union {
1723 struct {
1724 le32 dataoffsl;
1725 le32 dataoffsh;
1726 } parts;
1727 le64 all;
1728 } realign;
1729 int found;
1730 size_t size;
1731 size_t rdsize;
1732 s64 offs;
1733 ntfs_inode *ni;
1734 ntfs_index_context *xsii;
1735 char *securattr;
1737 securattr = (char*)NULL;
1738 ni = vol->secure_ni;
1739 xsii = vol->secure_xsii;
1740 if (ni && xsii) {
1741 ntfs_index_ctx_reinit(xsii);
1742 found =
1743 !ntfs_index_lookup((char*)&id,
1744 sizeof(SII_INDEX_KEY), xsii);
1745 if (found) {
1746 psii = (struct SII*)xsii->entry;
1747 size =
1748 (size_t) le32_to_cpu(psii->datasize)
1749 - sizeof(SECURITY_DESCRIPTOR_HEADER);
1750 /* work around bad alignment problem */
1751 realign.parts.dataoffsh = psii->dataoffsh;
1752 realign.parts.dataoffsl = psii->dataoffsl;
1753 offs = le64_to_cpu(realign.all)
1754 + sizeof(SECURITY_DESCRIPTOR_HEADER);
1756 securattr = (char*)ntfs_malloc(size);
1757 if (securattr) {
1758 rdsize = ntfs_local_read(
1759 ni, STREAM_SDS, 4,
1760 securattr, size, offs);
1761 if ((rdsize != size)
1762 || !ntfs_valid_descr(securattr,
1763 rdsize)) {
1764 /* error to be logged by caller */
1765 free(securattr);
1766 securattr = (char*)NULL;
1769 } else
1770 if (errno != ENOENT)
1771 ntfs_log_perror("Inconsistency in index $SII");
1773 if (!securattr) {
1774 ntfs_log_error("Failed to retrieve a security descriptor\n");
1775 errno = EIO;
1777 return (securattr);
1781 * Get the security descriptor associated to a file
1783 * Either :
1784 * - read the security descriptor attribute (v1.x format)
1785 * - or find the descriptor in $Secure:$SDS (v3.x format)
1787 * in both case, sanity checks are done on the attribute and
1788 * the descriptor can be assumed safe
1790 * The returned descriptor is dynamically allocated and has to be freed
1793 static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
1795 SII_INDEX_KEY securid;
1796 char *securattr;
1797 s64 readallsz;
1800 * Warning : in some situations, after fixing by chkdsk,
1801 * v3_Extensions are marked present (long standard informations)
1802 * with a default security descriptor inserted in an
1803 * attribute
1805 if (test_nino_flag(ni, v3_Extensions)
1806 && vol->secure_ni && ni->security_id) {
1807 /* get v3.x descriptor in $Secure */
1808 securid.security_id = ni->security_id;
1809 securattr = retrievesecurityattr(vol,securid);
1810 if (!securattr)
1811 ntfs_log_error("Bad security descriptor for 0x%lx\n",
1812 (long)le32_to_cpu(ni->security_id));
1813 } else {
1814 /* get v1.x security attribute */
1815 readallsz = 0;
1816 securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
1817 AT_UNNAMED, 0, &readallsz);
1818 if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
1819 ntfs_log_error("Bad security descriptor for inode %lld\n",
1820 (long long)ni->mft_no);
1821 free(securattr);
1822 securattr = (char*)NULL;
1825 if (!securattr) {
1827 * in some situations, there is no security
1828 * descriptor, and chkdsk does not detect or fix
1829 * anything. This could be a normal situation.
1830 * When this happens, simulate a descriptor with
1831 * minimum rights, so that a real descriptor can
1832 * be created by chown or chmod
1834 ntfs_log_error("No security descriptor found for inode %lld\n",
1835 (long long)ni->mft_no);
1836 securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
1838 return (securattr);
1841 #if POSIXACLS
1844 * Determine which access types to a file are allowed
1845 * according to the relation of current process to the file
1847 * Do not call if default_permissions is set
1850 static int access_check_posix(struct SECURITY_CONTEXT *scx,
1851 struct POSIX_SECURITY *pxdesc, mode_t request,
1852 uid_t uid, gid_t gid)
1854 struct POSIX_ACE *pxace;
1855 int userperms;
1856 int groupperms;
1857 int mask;
1858 BOOL somegroup;
1859 BOOL needgroups;
1860 mode_t perms;
1861 int i;
1863 perms = pxdesc->mode;
1864 /* owner and root access */
1865 if (!scx->uid || (uid == scx->uid)) {
1866 if (!scx->uid) {
1867 /* root access if owner or other execution */
1868 if (perms & 0101)
1869 perms = 07777;
1870 else {
1871 /* root access if some group execution */
1872 groupperms = 0;
1873 mask = 7;
1874 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1875 pxace = &pxdesc->acl.ace[i];
1876 switch (pxace->tag) {
1877 case POSIX_ACL_USER_OBJ :
1878 case POSIX_ACL_GROUP_OBJ :
1879 case POSIX_ACL_GROUP :
1880 groupperms |= pxace->perms;
1881 break;
1882 case POSIX_ACL_MASK :
1883 mask = pxace->perms & 7;
1884 break;
1885 default :
1886 break;
1889 perms = (groupperms & mask & 1) | 6;
1891 } else
1892 perms &= 07700;
1893 } else {
1895 * analyze designated users, get mask
1896 * and identify whether we need to check
1897 * the group memberships. The groups are
1898 * not needed when all groups have the
1899 * same permissions as other for the
1900 * requested modes.
1902 userperms = -1;
1903 groupperms = -1;
1904 needgroups = FALSE;
1905 mask = 7;
1906 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1907 pxace = &pxdesc->acl.ace[i];
1908 switch (pxace->tag) {
1909 case POSIX_ACL_USER :
1910 if ((uid_t)pxace->id == scx->uid)
1911 userperms = pxace->perms;
1912 break;
1913 case POSIX_ACL_MASK :
1914 mask = pxace->perms & 7;
1915 break;
1916 case POSIX_ACL_GROUP_OBJ :
1917 case POSIX_ACL_GROUP :
1918 if (((pxace->perms & mask) ^ perms)
1919 & (request >> 6) & 7)
1920 needgroups = TRUE;
1921 break;
1922 default :
1923 break;
1926 /* designated users */
1927 if (userperms >= 0)
1928 perms = (perms & 07000) + (userperms & mask);
1929 else if (!needgroups)
1930 perms &= 07007;
1931 else {
1932 /* owning group */
1933 if (!(~(perms >> 3) & request & mask)
1934 && ((gid == scx->gid)
1935 || groupmember(scx, scx->uid, gid)))
1936 perms &= 07070;
1937 else {
1938 /* other groups */
1939 groupperms = -1;
1940 somegroup = FALSE;
1941 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1942 pxace = &pxdesc->acl.ace[i];
1943 if ((pxace->tag == POSIX_ACL_GROUP)
1944 && groupmember(scx, uid, pxace->id)) {
1945 if (!(~pxace->perms & request & mask))
1946 groupperms = pxace->perms;
1947 somegroup = TRUE;
1950 if (groupperms >= 0)
1951 perms = (perms & 07000) + (groupperms & mask);
1952 else
1953 if (somegroup)
1954 perms = 0;
1955 else
1956 perms &= 07007;
1960 return (perms);
1964 * Get permissions to access a file
1965 * Takes into account the relation of user to file (owner, group, ...)
1966 * Do no use as mode of the file
1967 * Do no call if default_permissions is set
1969 * returns -1 if there is a problem
1972 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
1973 ntfs_inode * ni, mode_t request)
1975 const SECURITY_DESCRIPTOR_RELATIVE *phead;
1976 const struct CACHED_PERMISSIONS *cached;
1977 char *securattr;
1978 const SID *usid; /* owner of file/directory */
1979 const SID *gsid; /* group of file/directory */
1980 uid_t uid;
1981 gid_t gid;
1982 int perm;
1983 BOOL isdir;
1984 struct POSIX_SECURITY *pxdesc;
1986 if (!scx->mapping[MAPUSERS])
1987 perm = 07777;
1988 else {
1989 /* check whether available in cache */
1990 cached = fetch_cache(scx,ni);
1991 if (cached) {
1992 uid = cached->uid;
1993 gid = cached->gid;
1994 perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
1995 } else {
1996 perm = 0; /* default to no permission */
1997 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
1998 != const_cpu_to_le16(0);
1999 securattr = getsecurityattr(scx->vol, ni);
2000 if (securattr) {
2001 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2002 securattr;
2003 gsid = (const SID*)&
2004 securattr[le32_to_cpu(phead->group)];
2005 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2006 #if OWNERFROMACL
2007 usid = ntfs_acl_owner(securattr);
2008 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2009 usid, gsid, isdir);
2010 if (pxdesc)
2011 perm = pxdesc->mode & 07777;
2012 else
2013 perm = -1;
2014 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2015 #else
2016 usid = (const SID*)&
2017 securattr[le32_to_cpu(phead->owner)];
2018 pxdesc = ntfs_build_permissions_posix(scx,securattr,
2019 usid, gsid, isdir);
2020 if (pxdesc)
2021 perm = pxdesc->mode & 07777;
2022 else
2023 perm = -1;
2024 if (!perm && ntfs_same_sid(usid, adminsid)) {
2025 uid = find_tenant(scx, securattr);
2026 if (uid)
2027 perm = 0700;
2028 } else
2029 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2030 #endif
2032 * Create a security id if there were none
2033 * and upgrade option is selected
2035 if (!test_nino_flag(ni, v3_Extensions)
2036 && (perm >= 0)
2037 && (scx->vol->secure_flags
2038 & (1 << SECURITY_ADDSECURIDS))) {
2039 upgrade_secur_desc(scx->vol,
2040 securattr, ni);
2042 * fetch owner and group for cacheing
2043 * if there is a securid
2046 if (test_nino_flag(ni, v3_Extensions)
2047 && (perm >= 0)) {
2048 enter_cache(scx, ni, uid,
2049 gid, pxdesc);
2051 if (pxdesc) {
2052 perm = access_check_posix(scx,pxdesc,request,uid,gid);
2053 free(pxdesc);
2055 free(securattr);
2056 } else {
2057 perm = -1;
2058 uid = gid = 0;
2062 return (perm);
2066 * Get a Posix ACL
2068 * returns size or -errno if there is a problem
2069 * if size was too small, no copy is done and errno is not set,
2070 * the caller is expected to issue a new call
2073 int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2074 const char *name, char *value, size_t size)
2076 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2077 struct POSIX_SECURITY *pxdesc;
2078 const struct CACHED_PERMISSIONS *cached;
2079 char *securattr;
2080 const SID *usid; /* owner of file/directory */
2081 const SID *gsid; /* group of file/directory */
2082 uid_t uid;
2083 gid_t gid;
2084 int perm;
2085 BOOL isdir;
2086 size_t outsize;
2088 outsize = 0; /* default to error */
2089 if (!scx->mapping[MAPUSERS])
2090 errno = ENOTSUP;
2091 else {
2092 /* check whether available in cache */
2093 cached = fetch_cache(scx,ni);
2094 if (cached)
2095 pxdesc = cached->pxdesc;
2096 else {
2097 securattr = getsecurityattr(scx->vol, ni);
2098 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2099 != const_cpu_to_le16(0);
2100 if (securattr) {
2101 phead =
2102 (const SECURITY_DESCRIPTOR_RELATIVE*)
2103 securattr;
2104 gsid = (const SID*)&
2105 securattr[le32_to_cpu(phead->group)];
2106 #if OWNERFROMACL
2107 usid = ntfs_acl_owner(securattr);
2108 #else
2109 usid = (const SID*)&
2110 securattr[le32_to_cpu(phead->owner)];
2111 #endif
2112 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2113 usid, gsid, isdir);
2116 * fetch owner and group for cacheing
2118 if (pxdesc) {
2119 perm = pxdesc->mode & 07777;
2121 * Create a security id if there were none
2122 * and upgrade option is selected
2124 if (!test_nino_flag(ni, v3_Extensions)
2125 && (scx->vol->secure_flags
2126 & (1 << SECURITY_ADDSECURIDS))) {
2127 upgrade_secur_desc(scx->vol,
2128 securattr, ni);
2130 #if OWNERFROMACL
2131 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2132 #else
2133 if (!perm && ntfs_same_sid(usid, adminsid)) {
2134 uid = find_tenant(scx,
2135 securattr);
2136 if (uid)
2137 perm = 0700;
2138 } else
2139 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2140 #endif
2141 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2142 if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
2143 enter_cache(scx, ni, uid,
2144 gid, pxdesc);
2146 free(securattr);
2147 } else
2148 pxdesc = (struct POSIX_SECURITY*)NULL;
2151 if (pxdesc) {
2152 if (ntfs_valid_posix(pxdesc)) {
2153 if (!strcmp(name,"system.posix_acl_default")) {
2154 if (ni->mrec->flags
2155 & MFT_RECORD_IS_DIRECTORY)
2156 outsize = sizeof(struct POSIX_ACL)
2157 + pxdesc->defcnt*sizeof(struct POSIX_ACE);
2158 else {
2160 * getting default ACL from plain file :
2161 * return EACCES if size > 0 as
2162 * indicated in the man, but return ok
2163 * if size == 0, so that ls does not
2164 * display an error
2166 if (size > 0) {
2167 outsize = 0;
2168 errno = EACCES;
2169 } else
2170 outsize = sizeof(struct POSIX_ACL);
2172 if (outsize && (outsize <= size)) {
2173 memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
2174 memcpy(&value[sizeof(struct POSIX_ACL)],
2175 &pxdesc->acl.ace[pxdesc->firstdef],
2176 outsize-sizeof(struct POSIX_ACL));
2178 } else {
2179 outsize = sizeof(struct POSIX_ACL)
2180 + pxdesc->acccnt*sizeof(struct POSIX_ACE);
2181 if (outsize <= size)
2182 memcpy(value,&pxdesc->acl,outsize);
2184 } else {
2185 outsize = 0;
2186 errno = EIO;
2187 ntfs_log_error("Invalid Posix ACL built\n");
2189 if (!cached)
2190 free(pxdesc);
2191 } else
2192 outsize = 0;
2194 return (outsize ? (int)outsize : -errno);
2197 #else /* POSIXACLS */
2201 * Get permissions to access a file
2202 * Takes into account the relation of user to file (owner, group, ...)
2203 * Do no use as mode of the file
2205 * returns -1 if there is a problem
2208 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
2209 ntfs_inode *ni, mode_t request)
2211 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2212 const struct CACHED_PERMISSIONS *cached;
2213 char *securattr;
2214 const SID *usid; /* owner of file/directory */
2215 const SID *gsid; /* group of file/directory */
2216 BOOL isdir;
2217 uid_t uid;
2218 gid_t gid;
2219 int perm;
2221 if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
2222 perm = 07777;
2223 else {
2224 /* check whether available in cache */
2225 cached = fetch_cache(scx,ni);
2226 if (cached) {
2227 perm = cached->mode;
2228 uid = cached->uid;
2229 gid = cached->gid;
2230 } else {
2231 perm = 0; /* default to no permission */
2232 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2233 != const_cpu_to_le16(0);
2234 securattr = getsecurityattr(scx->vol, ni);
2235 if (securattr) {
2236 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2237 securattr;
2238 gsid = (const SID*)&
2239 securattr[le32_to_cpu(phead->group)];
2240 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2241 #if OWNERFROMACL
2242 usid = ntfs_acl_owner(securattr);
2243 perm = ntfs_build_permissions(securattr,
2244 usid, gsid, isdir);
2245 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2246 #else
2247 usid = (const SID*)&
2248 securattr[le32_to_cpu(phead->owner)];
2249 perm = ntfs_build_permissions(securattr,
2250 usid, gsid, isdir);
2251 if (!perm && ntfs_same_sid(usid, adminsid)) {
2252 uid = find_tenant(scx, securattr);
2253 if (uid)
2254 perm = 0700;
2255 } else
2256 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2257 #endif
2259 * Create a security id if there were none
2260 * and upgrade option is selected
2262 if (!test_nino_flag(ni, v3_Extensions)
2263 && (perm >= 0)
2264 && (scx->vol->secure_flags
2265 & (1 << SECURITY_ADDSECURIDS))) {
2266 upgrade_secur_desc(scx->vol,
2267 securattr, ni);
2269 * fetch owner and group for cacheing
2270 * if there is a securid
2273 if (test_nino_flag(ni, v3_Extensions)
2274 && (perm >= 0)) {
2275 enter_cache(scx, ni, uid,
2276 gid, perm);
2278 free(securattr);
2279 } else {
2280 perm = -1;
2281 uid = gid = 0;
2284 if (perm >= 0) {
2285 if (!scx->uid) {
2286 /* root access and execution */
2287 if (perm & 0111)
2288 perm = 07777;
2289 else
2290 perm = 0;
2291 } else
2292 if (uid == scx->uid)
2293 perm &= 07700;
2294 else
2296 * avoid checking group membership
2297 * when the requested perms for group
2298 * are the same as perms for other
2300 if ((gid == scx->gid)
2301 || ((((perm >> 3) ^ perm)
2302 & (request >> 6) & 7)
2303 && groupmember(scx, scx->uid, gid)))
2304 perm &= 07070;
2305 else
2306 perm &= 07007;
2309 return (perm);
2312 #endif /* POSIXACLS */
2315 * Get an NTFS ACL
2317 * Returns size or -errno if there is a problem
2318 * if size was too small, no copy is done and errno is not set,
2319 * the caller is expected to issue a new call
2322 int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2323 char *value, size_t size)
2325 char *securattr;
2326 size_t outsize;
2328 outsize = 0; /* default to no data and no error */
2329 securattr = getsecurityattr(scx->vol, ni);
2330 if (securattr) {
2331 outsize = ntfs_attr_size(securattr);
2332 if (outsize <= size) {
2333 memcpy(value,securattr,outsize);
2335 free(securattr);
2337 return (outsize ? (int)outsize : -errno);
2341 * Get owner, group and permissions in an stat structure
2342 * returns permissions, or -1 if there is a problem
2345 int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
2346 ntfs_inode * ni, struct stat *stbuf)
2348 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2349 char *securattr;
2350 const SID *usid; /* owner of file/directory */
2351 const SID *gsid; /* group of file/directory */
2352 const struct CACHED_PERMISSIONS *cached;
2353 int perm;
2354 BOOL isdir;
2355 #if POSIXACLS
2356 struct POSIX_SECURITY *pxdesc;
2357 #endif
2359 if (!scx->mapping[MAPUSERS])
2360 perm = 07777;
2361 else {
2362 /* check whether available in cache */
2363 cached = fetch_cache(scx,ni);
2364 if (cached) {
2365 perm = cached->mode;
2366 stbuf->st_uid = cached->uid;
2367 stbuf->st_gid = cached->gid;
2368 stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
2369 } else {
2370 perm = -1; /* default to error */
2371 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2372 != const_cpu_to_le16(0);
2373 securattr = getsecurityattr(scx->vol, ni);
2374 if (securattr) {
2375 phead =
2376 (const SECURITY_DESCRIPTOR_RELATIVE*)
2377 securattr;
2378 gsid = (const SID*)&
2379 securattr[le32_to_cpu(phead->group)];
2380 #if OWNERFROMACL
2381 usid = ntfs_acl_owner(securattr);
2382 #else
2383 usid = (const SID*)&
2384 securattr[le32_to_cpu(phead->owner)];
2385 #endif
2386 #if POSIXACLS
2387 pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr,
2388 usid, gsid, isdir);
2389 if (pxdesc)
2390 perm = pxdesc->mode & 07777;
2391 else
2392 perm = -1;
2393 #else
2394 perm = ntfs_build_permissions(securattr,
2395 usid, gsid, isdir);
2396 #endif
2398 * fetch owner and group for cacheing
2400 if (perm >= 0) {
2402 * Create a security id if there were none
2403 * and upgrade option is selected
2405 if (!test_nino_flag(ni, v3_Extensions)
2406 && (scx->vol->secure_flags
2407 & (1 << SECURITY_ADDSECURIDS))) {
2408 upgrade_secur_desc(scx->vol,
2409 securattr, ni);
2411 #if OWNERFROMACL
2412 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2413 #else
2414 if (!perm && ntfs_same_sid(usid, adminsid)) {
2415 stbuf->st_uid =
2416 find_tenant(scx,
2417 securattr);
2418 if (stbuf->st_uid)
2419 perm = 0700;
2420 } else
2421 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2422 #endif
2423 stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2424 stbuf->st_mode =
2425 (stbuf->st_mode & ~07777) + perm;
2426 #if POSIXACLS
2427 enter_cache(scx, ni, stbuf->st_uid,
2428 stbuf->st_gid, pxdesc);
2429 free(pxdesc);
2430 #else
2431 enter_cache(scx, ni, stbuf->st_uid,
2432 stbuf->st_gid, perm);
2433 #endif
2435 free(securattr);
2439 return (perm);
2442 #if POSIXACLS
2445 * Get the base for a Posix inheritance and
2446 * build an inherited Posix descriptor
2449 static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
2450 ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
2452 const struct CACHED_PERMISSIONS *cached;
2453 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2454 struct POSIX_SECURITY *pxdesc;
2455 struct POSIX_SECURITY *pydesc;
2456 char *securattr;
2457 const SID *usid;
2458 const SID *gsid;
2459 uid_t uid;
2460 gid_t gid;
2462 pydesc = (struct POSIX_SECURITY*)NULL;
2463 /* check whether parent directory is available in cache */
2464 cached = fetch_cache(scx,dir_ni);
2465 if (cached) {
2466 uid = cached->uid;
2467 gid = cached->gid;
2468 pxdesc = cached->pxdesc;
2469 if (pxdesc) {
2470 pydesc = ntfs_build_inherited_posix(pxdesc,mode,
2471 scx->umask,isdir);
2473 } else {
2474 securattr = getsecurityattr(scx->vol, dir_ni);
2475 if (securattr) {
2476 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2477 securattr;
2478 gsid = (const SID*)&
2479 securattr[le32_to_cpu(phead->group)];
2480 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2481 #if OWNERFROMACL
2482 usid = ntfs_acl_owner(securattr);
2483 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2484 usid, gsid, TRUE);
2485 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2486 #else
2487 usid = (const SID*)&
2488 securattr[le32_to_cpu(phead->owner)];
2489 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2490 usid, gsid, TRUE);
2491 if (pxdesc && ntfs_same_sid(usid, adminsid)) {
2492 uid = find_tenant(scx, securattr);
2493 } else
2494 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2495 #endif
2496 if (pxdesc) {
2498 * Create a security id if there were none
2499 * and upgrade option is selected
2501 if (!test_nino_flag(dir_ni, v3_Extensions)
2502 && (scx->vol->secure_flags
2503 & (1 << SECURITY_ADDSECURIDS))) {
2504 upgrade_secur_desc(scx->vol,
2505 securattr, dir_ni);
2507 * fetch owner and group for cacheing
2508 * if there is a securid
2511 if (test_nino_flag(dir_ni, v3_Extensions)) {
2512 enter_cache(scx, dir_ni, uid,
2513 gid, pxdesc);
2515 pydesc = ntfs_build_inherited_posix(pxdesc,
2516 mode, scx->umask, isdir);
2517 free(pxdesc);
2519 free(securattr);
2522 return (pydesc);
2526 * Allocate a security_id for a file being created
2528 * Returns zero if not possible (NTFS v3.x required)
2531 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2532 uid_t uid, gid_t gid, ntfs_inode *dir_ni,
2533 mode_t mode, BOOL isdir)
2535 #if !FORCE_FORMAT_v1x
2536 const struct CACHED_SECURID *cached;
2537 struct CACHED_SECURID wanted;
2538 struct POSIX_SECURITY *pxdesc;
2539 char *newattr;
2540 int newattrsz;
2541 const SID *usid;
2542 const SID *gsid;
2543 BIGSID defusid;
2544 BIGSID defgsid;
2545 le32 securid;
2546 #endif
2548 securid = const_cpu_to_le32(0);
2550 #if !FORCE_FORMAT_v1x
2552 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2553 if (pxdesc) {
2554 /* check whether target securid is known in cache */
2556 wanted.uid = uid;
2557 wanted.gid = gid;
2558 wanted.dmode = pxdesc->mode & mode & 07777;
2559 if (isdir) wanted.dmode |= 0x10000;
2560 wanted.variable = (void*)pxdesc;
2561 wanted.varsize = sizeof(struct POSIX_SECURITY)
2562 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2563 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2564 scx->vol->securid_cache, GENERIC(&wanted),
2565 (cache_compare)compare);
2566 /* quite simple, if we are lucky */
2567 if (cached)
2568 securid = cached->securid;
2570 /* not in cache : make sure we can create ids */
2572 if (!cached && (scx->vol->major_ver >= 3)) {
2573 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2574 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2575 if (!usid || !gsid) {
2576 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2577 (int)uid, (int)gid);
2578 usid = gsid = adminsid;
2580 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2581 isdir, usid, gsid);
2582 if (newattr) {
2583 newattrsz = ntfs_attr_size(newattr);
2584 securid = setsecurityattr(scx->vol,
2585 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2586 newattrsz);
2587 if (securid) {
2588 /* update cache, for subsequent use */
2589 wanted.securid = securid;
2590 ntfs_enter_cache(scx->vol->securid_cache,
2591 GENERIC(&wanted),
2592 (cache_compare)compare);
2594 free(newattr);
2595 } else {
2597 * could not build new security attribute
2598 * errno set by ntfs_build_descr()
2602 free(pxdesc);
2604 #endif
2605 return (securid);
2609 * Apply Posix inheritance to a newly created file
2610 * (for NTFS 1.x only : no securid)
2613 int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
2614 ntfs_inode *ni, uid_t uid, gid_t gid,
2615 ntfs_inode *dir_ni, mode_t mode)
2617 struct POSIX_SECURITY *pxdesc;
2618 char *newattr;
2619 const SID *usid;
2620 const SID *gsid;
2621 BIGSID defusid;
2622 BIGSID defgsid;
2623 BOOL isdir;
2624 int res;
2626 res = -1;
2627 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2628 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2629 if (pxdesc) {
2630 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2631 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2632 if (!usid || !gsid) {
2633 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2634 (int)uid, (int)gid);
2635 usid = gsid = adminsid;
2637 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2638 isdir, usid, gsid);
2639 if (newattr) {
2640 /* Adjust Windows read-only flag */
2641 res = update_secur_descr(scx->vol, newattr, ni);
2642 if (!res) {
2643 if (mode & S_IWUSR)
2644 ni->flags &= ~FILE_ATTR_READONLY;
2645 else
2646 ni->flags |= FILE_ATTR_READONLY;
2648 #if CACHE_LEGACY_SIZE
2649 /* also invalidate legacy cache */
2650 if (isdir && !ni->security_id) {
2651 struct CACHED_PERMISSIONS_LEGACY legacy;
2653 legacy.mft_no = ni->mft_no;
2654 legacy.variable = pxdesc;
2655 legacy.varsize = sizeof(struct POSIX_SECURITY)
2656 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2657 ntfs_invalidate_cache(scx->vol->legacy_cache,
2658 GENERIC(&legacy),
2659 (cache_compare)leg_compare,0);
2661 #endif
2662 free(newattr);
2664 } else {
2666 * could not build new security attribute
2667 * errno set by ntfs_build_descr()
2671 return (res);
2674 #else
2676 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2677 uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
2679 #if !FORCE_FORMAT_v1x
2680 const struct CACHED_SECURID *cached;
2681 struct CACHED_SECURID wanted;
2682 char *newattr;
2683 int newattrsz;
2684 const SID *usid;
2685 const SID *gsid;
2686 BIGSID defusid;
2687 BIGSID defgsid;
2688 le32 securid;
2689 #endif
2691 securid = const_cpu_to_le32(0);
2693 #if !FORCE_FORMAT_v1x
2694 /* check whether target securid is known in cache */
2696 wanted.uid = uid;
2697 wanted.gid = gid;
2698 wanted.dmode = mode & 07777;
2699 if (isdir) wanted.dmode |= 0x10000;
2700 wanted.variable = (void*)NULL;
2701 wanted.varsize = 0;
2702 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2703 scx->vol->securid_cache, GENERIC(&wanted),
2704 (cache_compare)compare);
2705 /* quite simple, if we are lucky */
2706 if (cached)
2707 securid = cached->securid;
2709 /* not in cache : make sure we can create ids */
2711 if (!cached && (scx->vol->major_ver >= 3)) {
2712 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2713 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2714 if (!usid || !gsid) {
2715 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2716 (int)uid, (int)gid);
2717 usid = gsid = adminsid;
2719 newattr = ntfs_build_descr(mode, isdir, usid, gsid);
2720 if (newattr) {
2721 newattrsz = ntfs_attr_size(newattr);
2722 securid = setsecurityattr(scx->vol,
2723 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2724 newattrsz);
2725 if (securid) {
2726 /* update cache, for subsequent use */
2727 wanted.securid = securid;
2728 ntfs_enter_cache(scx->vol->securid_cache,
2729 GENERIC(&wanted),
2730 (cache_compare)compare);
2732 free(newattr);
2733 } else {
2735 * could not build new security attribute
2736 * errno set by ntfs_build_descr()
2740 #endif
2741 return (securid);
2744 #endif
2747 * Update ownership and mode of a file, reusing an existing
2748 * security descriptor when possible
2750 * Returns zero if successful
2753 #if POSIXACLS
2754 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2755 uid_t uid, gid_t gid, mode_t mode,
2756 struct POSIX_SECURITY *pxdesc)
2757 #else
2758 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2759 uid_t uid, gid_t gid, mode_t mode)
2760 #endif
2762 int res;
2763 const struct CACHED_SECURID *cached;
2764 struct CACHED_SECURID wanted;
2765 char *newattr;
2766 const SID *usid;
2767 const SID *gsid;
2768 BIGSID defusid;
2769 BIGSID defgsid;
2770 BOOL isdir;
2772 res = 0;
2774 /* check whether target securid is known in cache */
2776 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2777 wanted.uid = uid;
2778 wanted.gid = gid;
2779 wanted.dmode = mode & 07777;
2780 if (isdir) wanted.dmode |= 0x10000;
2781 #if POSIXACLS
2782 wanted.variable = (void*)pxdesc;
2783 if (pxdesc)
2784 wanted.varsize = sizeof(struct POSIX_SECURITY)
2785 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2786 else
2787 wanted.varsize = 0;
2788 #else
2789 wanted.variable = (void*)NULL;
2790 wanted.varsize = 0;
2791 #endif
2792 if (test_nino_flag(ni, v3_Extensions)) {
2793 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2794 scx->vol->securid_cache, GENERIC(&wanted),
2795 (cache_compare)compare);
2796 /* quite simple, if we are lucky */
2797 if (cached) {
2798 ni->security_id = cached->securid;
2799 NInoSetDirty(ni);
2801 } else cached = (struct CACHED_SECURID*)NULL;
2803 if (!cached) {
2805 * Do not use usid and gsid from former attributes,
2806 * but recompute them to get repeatable results
2807 * which can be kept in cache.
2809 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2810 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2811 if (!usid || !gsid) {
2812 ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
2813 uid, gid);
2814 usid = gsid = adminsid;
2816 #if POSIXACLS
2817 if (pxdesc)
2818 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2819 isdir, usid, gsid);
2820 else
2821 newattr = ntfs_build_descr(mode,
2822 isdir, usid, gsid);
2823 #else
2824 newattr = ntfs_build_descr(mode,
2825 isdir, usid, gsid);
2826 #endif
2827 if (newattr) {
2828 res = update_secur_descr(scx->vol, newattr, ni);
2829 if (!res) {
2830 /* adjust Windows read-only flag */
2831 if (mode & S_IWUSR)
2832 ni->flags &= ~FILE_ATTR_READONLY;
2833 else
2834 ni->flags |= FILE_ATTR_READONLY;
2835 /* update cache, for subsequent use */
2836 if (test_nino_flag(ni, v3_Extensions)) {
2837 wanted.securid = ni->security_id;
2838 ntfs_enter_cache(scx->vol->securid_cache,
2839 GENERIC(&wanted),
2840 (cache_compare)compare);
2842 #if CACHE_LEGACY_SIZE
2843 /* also invalidate legacy cache */
2844 if (isdir && !ni->security_id) {
2845 struct CACHED_PERMISSIONS_LEGACY legacy;
2847 legacy.mft_no = ni->mft_no;
2848 #if POSIXACLS
2849 legacy.variable = wanted.variable;
2850 legacy.varsize = wanted.varsize;
2851 #else
2852 legacy.variable = (void*)NULL;
2853 legacy.varsize = 0;
2854 #endif
2855 ntfs_invalidate_cache(scx->vol->legacy_cache,
2856 GENERIC(&legacy),
2857 (cache_compare)leg_compare,0);
2859 #endif
2861 free(newattr);
2862 } else {
2864 * could not build new security attribute
2865 * errno set by ntfs_build_descr()
2867 res = -1;
2870 return (res);
2874 * Check whether user has ownership rights on a file
2876 * Returns TRUE if allowed
2877 * if not, errno tells why
2880 BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
2882 const struct CACHED_PERMISSIONS *cached;
2883 char *oldattr;
2884 const SID *usid;
2885 uid_t processuid;
2886 uid_t uid;
2887 BOOL gotowner;
2888 int allowed;
2890 processuid = scx->uid;
2891 /* TODO : use CAP_FOWNER process capability */
2893 * Always allow for root
2894 * Also always allow if no mapping has been defined
2896 if (!scx->mapping[MAPUSERS] || !processuid)
2897 allowed = TRUE;
2898 else {
2899 gotowner = FALSE; /* default */
2900 /* get the owner, either from cache or from old attribute */
2901 cached = fetch_cache(scx, ni);
2902 if (cached) {
2903 uid = cached->uid;
2904 gotowner = TRUE;
2905 } else {
2906 oldattr = getsecurityattr(scx->vol, ni);
2907 if (oldattr) {
2908 #if OWNERFROMACL
2909 usid = ntfs_acl_owner(oldattr);
2910 #else
2911 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2913 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2914 oldattr;
2915 usid = (const SID*)&oldattr
2916 [le32_to_cpu(phead->owner)];
2917 #endif
2918 uid = ntfs_find_user(scx->mapping[MAPUSERS],
2919 usid);
2920 gotowner = TRUE;
2921 free(oldattr);
2924 allowed = FALSE;
2925 if (gotowner) {
2926 /* TODO : use CAP_FOWNER process capability */
2927 if (!processuid || (processuid == uid))
2928 allowed = TRUE;
2929 else
2930 errno = EPERM;
2933 return (allowed);
2936 #ifdef HAVE_SETXATTR /* extended attributes interface required */
2938 #if POSIXACLS
2941 * Set a new access or default Posix ACL to a file
2942 * (or remove ACL if no input data)
2943 * Validity of input data is checked after merging
2945 * Returns 0, or -1 if there is a problem which errno describes
2948 int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2949 const char *name, const char *value, size_t size,
2950 int flags)
2952 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2953 const struct CACHED_PERMISSIONS *cached;
2954 char *oldattr;
2955 uid_t processuid;
2956 const SID *usid;
2957 const SID *gsid;
2958 uid_t uid;
2959 uid_t gid;
2960 int res;
2961 mode_t mode;
2962 BOOL isdir;
2963 BOOL deflt;
2964 BOOL exist;
2965 int count;
2966 struct POSIX_SECURITY *oldpxdesc;
2967 struct POSIX_SECURITY *newpxdesc;
2969 /* get the current pxsec, either from cache or from old attribute */
2970 res = -1;
2971 deflt = !strcmp(name,"system.posix_acl_default");
2972 if (size)
2973 count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
2974 else
2975 count = 0;
2976 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2977 newpxdesc = (struct POSIX_SECURITY*)NULL;
2978 if (!deflt || isdir || !size) {
2979 cached = fetch_cache(scx, ni);
2980 if (cached) {
2981 uid = cached->uid;
2982 gid = cached->gid;
2983 oldpxdesc = cached->pxdesc;
2984 if (oldpxdesc) {
2985 mode = oldpxdesc->mode;
2986 newpxdesc = ntfs_replace_acl(oldpxdesc,
2987 (const struct POSIX_ACL*)value,count,deflt);
2989 } else {
2990 oldattr = getsecurityattr(scx->vol, ni);
2991 if (oldattr) {
2992 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
2993 #if OWNERFROMACL
2994 usid = ntfs_acl_owner(oldattr);
2995 #else
2996 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
2997 #endif
2998 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
2999 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3000 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3001 oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
3002 oldattr, usid, gsid, isdir);
3003 if (oldpxdesc) {
3004 if (deflt)
3005 exist = oldpxdesc->defcnt > 0;
3006 else
3007 exist = oldpxdesc->acccnt > 3;
3008 if ((exist && (flags & XATTR_CREATE))
3009 || (!exist && (flags & XATTR_REPLACE))) {
3010 errno = (exist ? EEXIST : ENODATA);
3011 } else {
3012 mode = oldpxdesc->mode;
3013 newpxdesc = ntfs_replace_acl(oldpxdesc,
3014 (const struct POSIX_ACL*)value,count,deflt);
3016 free(oldpxdesc);
3018 free(oldattr);
3021 } else
3022 errno = EINVAL;
3024 if (newpxdesc) {
3025 processuid = scx->uid;
3026 /* TODO : use CAP_FOWNER process capability */
3027 if (!processuid || (uid == processuid)) {
3029 * clear setgid if file group does
3030 * not match process group
3032 if (processuid && (gid != scx->gid)
3033 && !groupmember(scx, scx->uid, gid)) {
3034 newpxdesc->mode &= ~S_ISGID;
3036 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3037 newpxdesc->mode, newpxdesc);
3038 } else
3039 errno = EPERM;
3040 free(newpxdesc);
3042 return (res ? -1 : 0);
3046 * Remove a default Posix ACL from a file
3048 * Returns 0, or -1 if there is a problem which errno describes
3051 int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3052 const char *name)
3054 return (ntfs_set_posix_acl(scx, ni, name,
3055 (const char*)NULL, 0, 0));
3058 #endif
3061 * Set a new NTFS ACL to a file
3063 * Returns 0, or -1 if there is a problem
3066 int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3067 const char *value, size_t size, int flags)
3069 char *attr;
3070 int res;
3072 res = -1;
3073 if ((size > 0)
3074 && !(flags & XATTR_CREATE)
3075 && ntfs_valid_descr(value,size)
3076 && (ntfs_attr_size(value) == size)) {
3077 /* need copying in order to write */
3078 attr = (char*)ntfs_malloc(size);
3079 if (attr) {
3080 memcpy(attr,value,size);
3081 res = update_secur_descr(scx->vol, attr, ni);
3083 * No need to invalidate standard caches :
3084 * the relation between a securid and
3085 * the associated protection is unchanged,
3086 * only the relation between a file and
3087 * its securid and protection is changed.
3089 #if CACHE_LEGACY_SIZE
3091 * we must however invalidate the legacy
3092 * cache, which is based on inode numbers.
3093 * For safety, invalidate even if updating
3094 * failed.
3096 if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3097 && !ni->security_id) {
3098 struct CACHED_PERMISSIONS_LEGACY legacy;
3100 legacy.mft_no = ni->mft_no;
3101 legacy.variable = (char*)NULL;
3102 legacy.varsize = 0;
3103 ntfs_invalidate_cache(scx->vol->legacy_cache,
3104 GENERIC(&legacy),
3105 (cache_compare)leg_compare,0);
3107 #endif
3108 free(attr);
3109 } else
3110 errno = ENOMEM;
3111 } else
3112 errno = EINVAL;
3113 return (res ? -1 : 0);
3116 #endif /* HAVE_SETXATTR */
3119 * Set new permissions to a file
3120 * Checks user mapping has been defined before request for setting
3122 * rejected if request is not originated by owner or root
3124 * returns 0 on success
3125 * -1 on failure, with errno = EIO
3128 int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
3130 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3131 const struct CACHED_PERMISSIONS *cached;
3132 char *oldattr;
3133 const SID *usid;
3134 const SID *gsid;
3135 uid_t processuid;
3136 uid_t uid;
3137 uid_t gid;
3138 int res;
3139 #if POSIXACLS
3140 BOOL isdir;
3141 int pxsize;
3142 const struct POSIX_SECURITY *oldpxdesc;
3143 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3144 #endif
3146 /* get the current owner, either from cache or from old attribute */
3147 res = 0;
3148 cached = fetch_cache(scx, ni);
3149 if (cached) {
3150 uid = cached->uid;
3151 gid = cached->gid;
3152 #if POSIXACLS
3153 oldpxdesc = cached->pxdesc;
3154 if (oldpxdesc) {
3155 /* must copy before merging */
3156 pxsize = sizeof(struct POSIX_SECURITY)
3157 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3158 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3159 if (newpxdesc) {
3160 memcpy(newpxdesc, oldpxdesc, pxsize);
3161 if (ntfs_merge_mode_posix(newpxdesc, mode))
3162 res = -1;
3163 } else
3164 res = -1;
3165 } else
3166 newpxdesc = (struct POSIX_SECURITY*)NULL;
3167 #endif
3168 } else {
3169 oldattr = getsecurityattr(scx->vol, ni);
3170 if (oldattr) {
3171 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
3172 #if OWNERFROMACL
3173 usid = ntfs_acl_owner(oldattr);
3174 #else
3175 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
3176 #endif
3177 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
3178 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3179 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3180 #if POSIXACLS
3181 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
3182 newpxdesc = ntfs_build_permissions_posix(scx->mapping,
3183 oldattr, usid, gsid, isdir);
3184 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3185 res = -1;
3186 #endif
3187 free(oldattr);
3188 } else
3189 res = -1;
3192 if (!res) {
3193 processuid = scx->uid;
3194 /* TODO : use CAP_FOWNER process capability */
3195 if (!processuid || (uid == processuid)) {
3197 * clear setgid if file group does
3198 * not match process group
3200 if (processuid && (gid != scx->gid)
3201 && !groupmember(scx, scx->uid, gid))
3202 mode &= ~S_ISGID;
3203 #if POSIXACLS
3204 if (newpxdesc) {
3205 newpxdesc->mode = mode;
3206 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3207 mode, newpxdesc);
3208 } else
3209 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3210 mode, newpxdesc);
3211 #else
3212 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3213 #endif
3214 } else {
3215 errno = EPERM;
3216 res = -1; /* neither owner nor root */
3218 } else {
3220 * Should not happen : a default descriptor is generated
3221 * by getsecurityattr() when there are none
3223 ntfs_log_error("File has no security descriptor\n");
3224 res = -1;
3225 errno = EIO;
3227 #if POSIXACLS
3228 if (newpxdesc) free(newpxdesc);
3229 #endif
3230 return (res ? -1 : 0);
3234 * Create a default security descriptor for files whose descriptor
3235 * cannot be inherited
3238 int ntfs_sd_add_everyone(ntfs_inode *ni)
3240 /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */
3241 SECURITY_DESCRIPTOR_RELATIVE *sd;
3242 ACL *acl;
3243 ACCESS_ALLOWED_ACE *ace;
3244 SID *sid;
3245 int ret, sd_len;
3247 /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
3249 * Calculate security descriptor length. We have 2 sub-authorities in
3250 * owner and group SIDs, but structure SID contain only one, so add
3251 * 4 bytes to every SID.
3253 sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
3254 sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
3255 sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
3256 if (!sd)
3257 return -1;
3259 sd->revision = SECURITY_DESCRIPTOR_REVISION;
3260 sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
3262 sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
3263 sid->revision = SID_REVISION;
3264 sid->sub_authority_count = 2;
3265 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3266 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3267 sid->identifier_authority.value[5] = 5;
3268 sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
3270 sid = (SID*)((u8*)sid + sizeof(SID) + 4);
3271 sid->revision = SID_REVISION;
3272 sid->sub_authority_count = 2;
3273 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3274 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3275 sid->identifier_authority.value[5] = 5;
3276 sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
3278 acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
3279 acl->revision = ACL_REVISION;
3280 acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
3281 acl->ace_count = const_cpu_to_le16(1);
3282 sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
3284 ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
3285 ace->type = ACCESS_ALLOWED_ACE_TYPE;
3286 ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
3287 ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
3288 ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */
3289 ace->sid.revision = SID_REVISION;
3290 ace->sid.sub_authority_count = 1;
3291 ace->sid.sub_authority[0] = const_cpu_to_le32(0);
3292 ace->sid.identifier_authority.value[5] = 1;
3294 ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
3295 sd_len);
3296 if (ret)
3297 ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
3299 free(sd);
3300 return ret;
3304 * Check whether user can access a file in a specific way
3306 * Returns 1 if access is allowed, including user is root or no
3307 * user mapping defined
3308 * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX
3309 * 0 and sets errno if there is a problem or if access
3310 * is not allowed
3312 * This is used for Posix ACL and checking creation of DOS file names
3315 int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
3316 ntfs_inode *ni,
3317 int accesstype) /* access type required (S_Ixxx values) */
3319 int perm;
3320 int res;
3321 int allow;
3322 struct stat stbuf;
3325 * Always allow for root unless execution is requested.
3326 * (was checked by fuse until kernel 2.6.29)
3327 * Also always allow if no mapping has been defined
3329 if (!scx->mapping[MAPUSERS]
3330 || (!scx->uid
3331 && (!(accesstype & S_IEXEC)
3332 || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
3333 allow = 1;
3334 else {
3335 perm = ntfs_get_perm(scx, ni, accesstype);
3336 if (perm >= 0) {
3337 res = EACCES;
3338 switch (accesstype) {
3339 case S_IEXEC:
3340 allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
3341 break;
3342 case S_IWRITE:
3343 allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
3344 break;
3345 case S_IWRITE + S_IEXEC:
3346 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3347 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3348 break;
3349 case S_IREAD:
3350 allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
3351 break;
3352 case S_IREAD + S_IEXEC:
3353 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3354 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3355 break;
3356 case S_IREAD + S_IWRITE:
3357 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3358 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
3359 break;
3360 case S_IWRITE + S_IEXEC + S_ISVTX:
3361 if (perm & S_ISVTX) {
3362 if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3363 && (stbuf.st_uid == scx->uid))
3364 allow = 1;
3365 else
3366 allow = 2;
3367 } else
3368 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3369 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3370 break;
3371 case S_IREAD + S_IWRITE + S_IEXEC:
3372 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3373 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3374 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3375 break;
3376 default :
3377 res = EINVAL;
3378 allow = 0;
3379 break;
3381 if (!allow)
3382 errno = res;
3383 } else
3384 allow = 0;
3386 return (allow);
3389 #if 0 /* not needed any more */
3392 * Check whether user can access the parent directory
3393 * of a file in a specific way
3395 * Returns true if access is allowed, including user is root and
3396 * no user mapping defined
3398 * Sets errno if there is a problem or if not allowed
3400 * This is used for Posix ACL and checking creation of DOS file names
3403 BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
3404 const char *path, int accesstype)
3406 int allow;
3407 char *dirpath;
3408 char *name;
3409 ntfs_inode *ni;
3410 ntfs_inode *dir_ni;
3411 struct stat stbuf;
3413 allow = 0;
3414 dirpath = strdup(path);
3415 if (dirpath) {
3416 /* the root of file system is seen as a parent of itself */
3417 /* is that correct ? */
3418 name = strrchr(dirpath, '/');
3419 *name = 0;
3420 dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
3421 if (dir_ni) {
3422 allow = ntfs_allowed_access(scx,
3423 dir_ni, accesstype);
3424 ntfs_inode_close(dir_ni);
3426 * for an not-owned sticky directory, have to
3427 * check whether file itself is owned
3429 if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
3430 && (allow == 2)) {
3431 ni = ntfs_pathname_to_inode(scx->vol, NULL,
3432 path);
3433 allow = FALSE;
3434 if (ni) {
3435 allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3436 && (stbuf.st_uid == scx->uid);
3437 ntfs_inode_close(ni);
3441 free(dirpath);
3443 return (allow); /* errno is set if not allowed */
3446 #endif
3449 * Define a new owner/group to a file
3451 * returns zero if successful
3454 int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3455 uid_t uid, gid_t gid)
3457 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3458 const struct CACHED_PERMISSIONS *cached;
3459 char *oldattr;
3460 const SID *usid;
3461 const SID *gsid;
3462 uid_t fileuid;
3463 uid_t filegid;
3464 mode_t mode;
3465 int perm;
3466 BOOL isdir;
3467 int res;
3468 #if POSIXACLS
3469 struct POSIX_SECURITY *pxdesc;
3470 BOOL pxdescbuilt = FALSE;
3471 #endif
3473 res = 0;
3474 /* get the current owner and mode from cache or security attributes */
3475 oldattr = (char*)NULL;
3476 cached = fetch_cache(scx,ni);
3477 if (cached) {
3478 fileuid = cached->uid;
3479 filegid = cached->gid;
3480 mode = cached->mode;
3481 #if POSIXACLS
3482 pxdesc = cached->pxdesc;
3483 if (!pxdesc)
3484 res = -1;
3485 #endif
3486 } else {
3487 fileuid = 0;
3488 filegid = 0;
3489 mode = 0;
3490 oldattr = getsecurityattr(scx->vol, ni);
3491 if (oldattr) {
3492 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3493 != const_cpu_to_le16(0);
3494 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3495 oldattr;
3496 gsid = (const SID*)
3497 &oldattr[le32_to_cpu(phead->group)];
3498 #if OWNERFROMACL
3499 usid = ntfs_acl_owner(oldattr);
3500 #else
3501 usid = (const SID*)
3502 &oldattr[le32_to_cpu(phead->owner)];
3503 #endif
3504 #if POSIXACLS
3505 pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3506 usid, gsid, isdir);
3507 if (pxdesc) {
3508 pxdescbuilt = TRUE;
3509 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3510 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3511 mode = perm = pxdesc->mode;
3512 } else
3513 res = -1;
3514 #else
3515 mode = perm = ntfs_build_permissions(oldattr,
3516 usid, gsid, isdir);
3517 if (perm >= 0) {
3518 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3519 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3520 } else
3521 res = -1;
3522 #endif
3523 free(oldattr);
3524 } else
3525 res = -1;
3527 if (!res) {
3528 /* check requested by root */
3529 /* or chgrp requested by owner to an owned group */
3530 if (!scx->uid
3531 || ((((int)uid < 0) || (uid == fileuid))
3532 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3533 && (fileuid == scx->uid))) {
3534 /* replace by the new usid and gsid */
3535 /* or reuse old gid and sid for cacheing */
3536 if ((int)uid < 0)
3537 uid = fileuid;
3538 if ((int)gid < 0)
3539 gid = filegid;
3540 /* clear setuid and setgid if owner has changed */
3541 /* unless request originated by root */
3542 if (uid && (fileuid != uid))
3543 mode &= 01777;
3544 #if POSIXACLS
3545 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3546 mode, pxdesc);
3547 #else
3548 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3549 #endif
3550 } else {
3551 res = -1; /* neither owner nor root */
3552 errno = EPERM;
3554 #if POSIXACLS
3555 if (pxdescbuilt)
3556 free(pxdesc);
3557 #endif
3558 } else {
3560 * Should not happen : a default descriptor is generated
3561 * by getsecurityattr() when there are none
3563 ntfs_log_error("File has no security descriptor\n");
3564 res = -1;
3565 errno = EIO;
3567 return (res ? -1 : 0);
3571 * Define new owner/group and mode to a file
3573 * returns zero if successful
3576 int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3577 uid_t uid, gid_t gid, const mode_t mode)
3579 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3580 const struct CACHED_PERMISSIONS *cached;
3581 char *oldattr;
3582 const SID *usid;
3583 const SID *gsid;
3584 uid_t fileuid;
3585 uid_t filegid;
3586 BOOL isdir;
3587 int res;
3588 #if POSIXACLS
3589 const struct POSIX_SECURITY *oldpxdesc;
3590 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3591 int pxsize;
3592 #endif
3594 res = 0;
3595 /* get the current owner and mode from cache or security attributes */
3596 oldattr = (char*)NULL;
3597 cached = fetch_cache(scx,ni);
3598 if (cached) {
3599 fileuid = cached->uid;
3600 filegid = cached->gid;
3601 #if POSIXACLS
3602 oldpxdesc = cached->pxdesc;
3603 if (oldpxdesc) {
3604 /* must copy before merging */
3605 pxsize = sizeof(struct POSIX_SECURITY)
3606 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3607 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3608 if (newpxdesc) {
3609 memcpy(newpxdesc, oldpxdesc, pxsize);
3610 if (ntfs_merge_mode_posix(newpxdesc, mode))
3611 res = -1;
3612 } else
3613 res = -1;
3615 #endif
3616 } else {
3617 fileuid = 0;
3618 filegid = 0;
3619 oldattr = getsecurityattr(scx->vol, ni);
3620 if (oldattr) {
3621 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3622 != const_cpu_to_le16(0);
3623 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3624 oldattr;
3625 gsid = (const SID*)
3626 &oldattr[le32_to_cpu(phead->group)];
3627 #if OWNERFROMACL
3628 usid = ntfs_acl_owner(oldattr);
3629 #else
3630 usid = (const SID*)
3631 &oldattr[le32_to_cpu(phead->owner)];
3632 #endif
3633 #if POSIXACLS
3634 newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3635 usid, gsid, isdir);
3636 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3637 res = -1;
3638 else {
3639 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3640 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3642 #endif
3643 free(oldattr);
3644 } else
3645 res = -1;
3647 if (!res) {
3648 /* check requested by root */
3649 /* or chgrp requested by owner to an owned group */
3650 if (!scx->uid
3651 || ((((int)uid < 0) || (uid == fileuid))
3652 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3653 && (fileuid == scx->uid))) {
3654 /* replace by the new usid and gsid */
3655 /* or reuse old gid and sid for cacheing */
3656 if ((int)uid < 0)
3657 uid = fileuid;
3658 if ((int)gid < 0)
3659 gid = filegid;
3660 #if POSIXACLS
3661 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3662 mode, newpxdesc);
3663 #else
3664 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3665 #endif
3666 } else {
3667 res = -1; /* neither owner nor root */
3668 errno = EPERM;
3670 } else {
3672 * Should not happen : a default descriptor is generated
3673 * by getsecurityattr() when there are none
3675 ntfs_log_error("File has no security descriptor\n");
3676 res = -1;
3677 errno = EIO;
3679 #if POSIXACLS
3680 free(newpxdesc);
3681 #endif
3682 return (res ? -1 : 0);
3686 * Build a security id for a descriptor inherited from
3687 * parent directory the Windows way
3690 static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
3691 const char *parentattr, BOOL fordir)
3693 const SECURITY_DESCRIPTOR_RELATIVE *pphead;
3694 const ACL *ppacl;
3695 const SID *usid;
3696 const SID *gsid;
3697 BIGSID defusid;
3698 BIGSID defgsid;
3699 int offpacl;
3700 int offowner;
3701 int offgroup;
3702 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
3703 ACL *pnacl;
3704 int parentattrsz;
3705 char *newattr;
3706 int newattrsz;
3707 int aclsz;
3708 int usidsz;
3709 int gsidsz;
3710 int pos;
3711 le32 securid;
3713 parentattrsz = ntfs_attr_size(parentattr);
3714 pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
3715 if (scx->mapping[MAPUSERS]) {
3716 usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
3717 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
3718 if (!usid)
3719 usid = adminsid;
3720 if (!gsid)
3721 gsid = adminsid;
3722 } else {
3724 * If there is no user mapping, we have to copy owner
3725 * and group from parent directory.
3726 * Windows never has to do that, because it can always
3727 * rely on a user mapping
3729 offowner = le32_to_cpu(pphead->owner);
3730 usid = (const SID*)&parentattr[offowner];
3731 offgroup = le32_to_cpu(pphead->group);
3732 gsid = (const SID*)&parentattr[offgroup];
3735 * new attribute is smaller than parent's
3736 * except for differences in SIDs which appear in
3737 * owner, group and possible grants and denials in
3738 * generic creator-owner and creator-group ACEs.
3739 * For directories, an ACE may be duplicated for
3740 * access and inheritance, so we double the count.
3742 usidsz = ntfs_sid_size(usid);
3743 gsidsz = ntfs_sid_size(gsid);
3744 newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
3745 if (fordir)
3746 newattrsz *= 2;
3747 newattr = (char*)ntfs_malloc(newattrsz);
3748 if (newattr) {
3749 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
3750 pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
3751 pnhead->alignment = 0;
3752 pnhead->control = SE_SELF_RELATIVE;
3753 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
3755 * locate and inherit DACL
3756 * do not test SE_DACL_PRESENT (wrong for "DR Watson")
3758 pnhead->dacl = const_cpu_to_le32(0);
3759 if (pphead->dacl) {
3760 offpacl = le32_to_cpu(pphead->dacl);
3761 ppacl = (const ACL*)&parentattr[offpacl];
3762 pnacl = (ACL*)&newattr[pos];
3763 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
3764 if (aclsz) {
3765 pnhead->dacl = cpu_to_le32(pos);
3766 pos += aclsz;
3767 pnhead->control |= SE_DACL_PRESENT;
3771 * locate and inherit SACL
3773 pnhead->sacl = const_cpu_to_le32(0);
3774 if (pphead->sacl) {
3775 offpacl = le32_to_cpu(pphead->sacl);
3776 ppacl = (const ACL*)&parentattr[offpacl];
3777 pnacl = (ACL*)&newattr[pos];
3778 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid, fordir);
3779 if (aclsz) {
3780 pnhead->sacl = cpu_to_le32(pos);
3781 pos += aclsz;
3782 pnhead->control |= SE_SACL_PRESENT;
3786 * inherit or redefine owner
3788 memcpy(&newattr[pos],usid,usidsz);
3789 pnhead->owner = cpu_to_le32(pos);
3790 pos += usidsz;
3792 * inherit or redefine group
3794 memcpy(&newattr[pos],gsid,gsidsz);
3795 pnhead->group = cpu_to_le32(pos);
3796 pos += usidsz;
3797 securid = setsecurityattr(scx->vol,
3798 (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
3799 free(newattr);
3800 } else
3801 securid = const_cpu_to_le32(0);
3802 return (securid);
3806 * Get an inherited security id
3808 * For Windows compatibility, the normal initial permission setting
3809 * may be inherited from the parent directory instead of being
3810 * defined by the creation arguments.
3812 * The following creates an inherited id for that purpose.
3814 * Note : the owner and group of parent directory are also
3815 * inherited (which is not the case on Windows) if no user mapping
3816 * is defined.
3818 * Returns the inherited id, or zero if not possible (eg on NTFS 1.x)
3821 le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
3822 ntfs_inode *dir_ni, BOOL fordir)
3824 struct CACHED_PERMISSIONS *cached;
3825 char *parentattr;
3826 le32 securid;
3828 securid = const_cpu_to_le32(0);
3829 cached = (struct CACHED_PERMISSIONS*)NULL;
3831 * Try to get inherited id from cache
3833 if (test_nino_flag(dir_ni, v3_Extensions)
3834 && dir_ni->security_id) {
3835 cached = fetch_cache(scx, dir_ni);
3836 if (cached)
3837 securid = (fordir ? cached->inh_dirid
3838 : cached->inh_fileid);
3841 * Not cached or not available in cache, compute it all
3842 * Note : if parent directory has no id, it is not cacheable
3844 if (!securid) {
3845 parentattr = getsecurityattr(scx->vol, dir_ni);
3846 if (parentattr) {
3847 securid = build_inherited_id(scx,
3848 parentattr, fordir);
3849 free(parentattr);
3851 * Store the result into cache for further use
3853 if (securid) {
3854 cached = fetch_cache(scx, dir_ni);
3855 if (cached) {
3856 if (fordir)
3857 cached->inh_dirid = securid;
3858 else
3859 cached->inh_fileid = securid;
3864 return (securid);
3868 * Link a group to a member of group
3870 * Returns 0 if OK, -1 (and errno set) if error
3873 static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
3874 gid_t gid)
3876 struct group *group;
3877 char **grmem;
3878 int grcnt;
3879 gid_t *groups;
3880 int res;
3882 res = 0;
3883 group = getgrgid(gid);
3884 if (group && group->gr_mem) {
3885 grcnt = usermapping->grcnt;
3886 groups = usermapping->groups;
3887 grmem = group->gr_mem;
3888 while (*grmem && strcmp(user->pw_name, *grmem))
3889 grmem++;
3890 if (*grmem) {
3891 if (!grcnt)
3892 groups = (gid_t*)malloc(sizeof(gid_t));
3893 else
3894 groups = (gid_t*)realloc(groups,
3895 (grcnt+1)*sizeof(gid_t));
3896 if (groups)
3897 groups[grcnt++] = gid;
3898 else {
3899 res = -1;
3900 errno = ENOMEM;
3903 usermapping->grcnt = grcnt;
3904 usermapping->groups = groups;
3906 return (res);
3911 * Statically link group to users
3912 * This is based on groups defined in /etc/group and does not take
3913 * the groups dynamically set by setgroups() nor any changes in
3914 * /etc/group into account
3916 * Only mapped groups and root group are linked to mapped users
3918 * Returns 0 if OK, -1 (and errno set) if error
3922 static int link_group_members(struct SECURITY_CONTEXT *scx)
3924 struct MAPPING *usermapping;
3925 struct MAPPING *groupmapping;
3926 struct passwd *user;
3927 int res;
3929 res = 0;
3930 for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
3931 usermapping=usermapping->next) {
3932 usermapping->grcnt = 0;
3933 usermapping->groups = (gid_t*)NULL;
3934 user = getpwuid(usermapping->xid);
3935 if (user && user->pw_name) {
3936 for (groupmapping=scx->mapping[MAPGROUPS];
3937 groupmapping && !res;
3938 groupmapping=groupmapping->next) {
3939 if (link_single_group(usermapping, user,
3940 groupmapping->xid))
3941 res = -1;
3943 if (!res && link_single_group(usermapping,
3944 user, (gid_t)0))
3945 res = -1;
3948 return (res);
3953 * Apply default single user mapping
3954 * returns zero if successful
3957 static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
3958 const SID *usid)
3960 struct MAPPING *usermapping;
3961 struct MAPPING *groupmapping;
3962 SID *sid;
3963 int sidsz;
3964 int res;
3966 res = -1;
3967 sidsz = ntfs_sid_size(usid);
3968 sid = (SID*)ntfs_malloc(sidsz);
3969 if (sid) {
3970 memcpy(sid,usid,sidsz);
3971 usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
3972 if (usermapping) {
3973 groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
3974 if (groupmapping) {
3975 usermapping->sid = sid;
3976 usermapping->xid = scx->uid;
3977 usermapping->next = (struct MAPPING*)NULL;
3978 groupmapping->sid = sid;
3979 groupmapping->xid = scx->uid;
3980 groupmapping->next = (struct MAPPING*)NULL;
3981 scx->mapping[MAPUSERS] = usermapping;
3982 scx->mapping[MAPGROUPS] = groupmapping;
3983 res = 0;
3987 return (res);
3992 * Make sure there are no ambiguous mapping
3993 * Ambiguous mapping may lead to undesired configurations and
3994 * we had rather be safe until the consequences are understood
3997 #if 0 /* not activated for now */
3999 static BOOL check_mapping(const struct MAPPING *usermapping,
4000 const struct MAPPING *groupmapping)
4002 const struct MAPPING *mapping1;
4003 const struct MAPPING *mapping2;
4004 BOOL ambiguous;
4006 ambiguous = FALSE;
4007 for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
4008 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
4009 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
4010 if (mapping1->xid != mapping2->xid)
4011 ambiguous = TRUE;
4012 } else {
4013 if (mapping1->xid == mapping2->xid)
4014 ambiguous = TRUE;
4016 for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
4017 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
4018 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
4019 if (mapping1->xid != mapping2->xid)
4020 ambiguous = TRUE;
4021 } else {
4022 if (mapping1->xid == mapping2->xid)
4023 ambiguous = TRUE;
4025 return (ambiguous);
4028 #endif
4031 * Try and apply default single user mapping
4032 * returns zero if successful
4035 static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
4037 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4038 ntfs_inode *ni;
4039 char *securattr;
4040 const SID *usid;
4041 int res;
4043 res = -1;
4044 ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
4045 if (ni) {
4046 securattr = getsecurityattr(scx->vol, ni);
4047 if (securattr) {
4048 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
4049 usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
4050 if (ntfs_is_user_sid(usid))
4051 res = ntfs_do_default_mapping(scx,usid);
4052 free(securattr);
4054 ntfs_inode_close(ni);
4056 return (res);
4060 * Basic read from a user mapping file on another volume
4063 static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
4065 return (read(*(int*)fileid, buf, size));
4070 * Read from a user mapping file on current NTFS partition
4073 static int localread(void *fileid, char *buf, size_t size, off_t offs)
4075 return (ntfs_local_read((ntfs_inode*)fileid,
4076 AT_UNNAMED, 0, buf, size, offs));
4080 * Build the user mapping
4081 * - according to a mapping file if defined (or default present),
4082 * - or try default single user mapping if possible
4084 * The mapping is specific to a mounted device
4085 * No locking done, mounting assumed non multithreaded
4087 * returns zero if mapping is successful
4088 * (failure should not be interpreted as an error)
4091 int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path)
4093 struct MAPLIST *item;
4094 struct MAPLIST *firstitem;
4095 struct MAPPING *usermapping;
4096 struct MAPPING *groupmapping;
4097 ntfs_inode *ni;
4098 int fd;
4100 /* be sure not to map anything until done */
4101 scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
4102 scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
4104 if (!usermap_path) usermap_path = MAPPINGFILE;
4105 if (usermap_path[0] == '/') {
4106 fd = open(usermap_path,O_RDONLY);
4107 if (fd > 0) {
4108 firstitem = ntfs_read_mapping(basicread, (void*)&fd);
4109 close(fd);
4110 } else
4111 firstitem = (struct MAPLIST*)NULL;
4112 } else {
4113 ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
4114 if (ni) {
4115 firstitem = ntfs_read_mapping(localread, ni);
4116 ntfs_inode_close(ni);
4117 } else
4118 firstitem = (struct MAPLIST*)NULL;
4122 if (firstitem) {
4123 usermapping = ntfs_do_user_mapping(firstitem);
4124 groupmapping = ntfs_do_group_mapping(firstitem);
4125 if (usermapping && groupmapping) {
4126 scx->mapping[MAPUSERS] = usermapping;
4127 scx->mapping[MAPGROUPS] = groupmapping;
4128 } else
4129 ntfs_log_error("There were no valid user or no valid group\n");
4130 /* now we can free the memory copy of input text */
4131 /* and rely on internal representation */
4132 while (firstitem) {
4133 item = firstitem->next;
4134 free(firstitem);
4135 firstitem = item;
4137 } else {
4138 /* no mapping file, try default mapping */
4139 if (scx->uid && scx->gid) {
4140 if (!ntfs_default_mapping(scx))
4141 ntfs_log_info("Using default user mapping\n");
4144 return (!scx->mapping[MAPUSERS] || link_group_members(scx));
4147 #ifdef HAVE_SETXATTR /* extended attributes interface required */
4150 * Get the ntfs attribute into an extended attribute
4151 * The attribute is returned according to cpu endianness
4154 int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
4156 u32 attrib;
4157 size_t outsize;
4159 outsize = 0; /* default to no data and no error */
4160 if (ni) {
4161 attrib = le32_to_cpu(ni->flags);
4162 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4163 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4164 else
4165 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4166 if (!attrib)
4167 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4168 outsize = sizeof(FILE_ATTR_FLAGS);
4169 if (size >= outsize) {
4170 if (value)
4171 memcpy(value,&attrib,outsize);
4172 else
4173 errno = EINVAL;
4176 return (outsize ? (int)outsize : -errno);
4180 * Return the ntfs attribute into an extended attribute
4181 * The attribute is expected according to cpu endianness
4183 * Returns 0, or -1 if there is a problem
4186 int ntfs_set_ntfs_attrib(ntfs_inode *ni,
4187 const char *value, size_t size, int flags)
4189 u32 attrib;
4190 le32 settable;
4191 ATTR_FLAGS dirflags;
4192 int res;
4194 res = -1;
4195 if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
4196 if (!(flags & XATTR_CREATE)) {
4197 /* copy to avoid alignment problems */
4198 memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
4199 settable = FILE_ATTR_SETTABLE;
4200 res = 0;
4201 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4203 * Accept changing compression for a directory
4204 * and set index root accordingly
4206 settable |= FILE_ATTR_COMPRESSED;
4207 if ((ni->flags ^ cpu_to_le32(attrib))
4208 & FILE_ATTR_COMPRESSED) {
4209 if (ni->flags & FILE_ATTR_COMPRESSED)
4210 dirflags = const_cpu_to_le16(0);
4211 else
4212 dirflags = ATTR_IS_COMPRESSED;
4213 res = ntfs_attr_set_flags(ni,
4214 AT_INDEX_ROOT,
4215 NTFS_INDEX_I30, 4,
4216 dirflags,
4217 ATTR_COMPRESSION_MASK);
4220 if (!res) {
4221 ni->flags = (ni->flags & ~settable)
4222 | (cpu_to_le32(attrib) & settable);
4223 NInoFileNameSetDirty(ni);
4224 NInoSetDirty(ni);
4226 } else
4227 errno = EEXIST;
4228 } else
4229 errno = EINVAL;
4230 return (res ? -1 : 0);
4233 #endif /* HAVE_SETXATTR */
4236 * Open $Secure once for all
4237 * returns zero if it succeeds
4238 * non-zero if it fails. This is not an error (on NTFS v1.x)
4242 int ntfs_open_secure(ntfs_volume *vol)
4244 ntfs_inode *ni;
4245 int res;
4247 res = -1;
4248 vol->secure_ni = (ntfs_inode*)NULL;
4249 vol->secure_xsii = (ntfs_index_context*)NULL;
4250 vol->secure_xsdh = (ntfs_index_context*)NULL;
4251 if (vol->major_ver >= 3) {
4252 /* make sure this is a genuine $Secure inode 9 */
4253 ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
4254 if (ni && (ni->mft_no == 9)) {
4255 vol->secure_reentry = 0;
4256 vol->secure_xsii = ntfs_index_ctx_get(ni,
4257 sii_stream, 4);
4258 vol->secure_xsdh = ntfs_index_ctx_get(ni,
4259 sdh_stream, 4);
4260 if (ni && vol->secure_xsii && vol->secure_xsdh) {
4261 vol->secure_ni = ni;
4262 res = 0;
4266 return (res);
4270 * Final cleaning
4271 * Allocated memory is freed to facilitate the detection of memory leaks
4274 void ntfs_close_secure(struct SECURITY_CONTEXT *scx)
4276 ntfs_volume *vol;
4278 vol = scx->vol;
4279 if (vol->secure_ni) {
4280 ntfs_index_ctx_put(vol->secure_xsii);
4281 ntfs_index_ctx_put(vol->secure_xsdh);
4282 ntfs_inode_close(vol->secure_ni);
4285 ntfs_free_mapping(scx->mapping);
4286 free_caches(scx);
4290 * API for direct access to security descriptors
4291 * based on Win32 API
4296 * Selective feeding of a security descriptor into user buffer
4298 * Returns TRUE if successful
4301 static BOOL feedsecurityattr(const char *attr, u32 selection,
4302 char *buf, u32 buflen, u32 *psize)
4304 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4305 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
4306 const ACL *pdacl;
4307 const ACL *psacl;
4308 const SID *pusid;
4309 const SID *pgsid;
4310 unsigned int offdacl;
4311 unsigned int offsacl;
4312 unsigned int offowner;
4313 unsigned int offgroup;
4314 unsigned int daclsz;
4315 unsigned int saclsz;
4316 unsigned int usidsz;
4317 unsigned int gsidsz;
4318 unsigned int size; /* size of requested attributes */
4319 BOOL ok;
4320 unsigned int pos;
4321 unsigned int avail;
4322 le16 control;
4324 avail = 0;
4325 control = SE_SELF_RELATIVE;
4326 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4327 size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4329 /* locate DACL if requested and available */
4330 if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
4331 offdacl = le32_to_cpu(phead->dacl);
4332 pdacl = (const ACL*)&attr[offdacl];
4333 daclsz = le16_to_cpu(pdacl->size);
4334 size += daclsz;
4335 avail |= DACL_SECURITY_INFORMATION;
4336 } else
4337 offdacl = daclsz = 0;
4339 /* locate owner if requested and available */
4340 offowner = le32_to_cpu(phead->owner);
4341 if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
4342 /* find end of USID */
4343 pusid = (const SID*)&attr[offowner];
4344 usidsz = ntfs_sid_size(pusid);
4345 size += usidsz;
4346 avail |= OWNER_SECURITY_INFORMATION;
4347 } else
4348 offowner = usidsz = 0;
4350 /* locate group if requested and available */
4351 offgroup = le32_to_cpu(phead->group);
4352 if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
4353 /* find end of GSID */
4354 pgsid = (const SID*)&attr[offgroup];
4355 gsidsz = ntfs_sid_size(pgsid);
4356 size += gsidsz;
4357 avail |= GROUP_SECURITY_INFORMATION;
4358 } else
4359 offgroup = gsidsz = 0;
4361 /* locate SACL if requested and available */
4362 if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
4363 /* find end of SACL */
4364 offsacl = le32_to_cpu(phead->sacl);
4365 psacl = (const ACL*)&attr[offsacl];
4366 saclsz = le16_to_cpu(psacl->size);
4367 size += saclsz;
4368 avail |= SACL_SECURITY_INFORMATION;
4369 } else
4370 offsacl = saclsz = 0;
4373 * Check having enough size in destination buffer
4374 * (required size is returned nevertheless so that
4375 * the request can be reissued with adequate size)
4377 if (size > buflen) {
4378 *psize = size;
4379 errno = EINVAL;
4380 ok = FALSE;
4381 } else {
4382 if (selection & OWNER_SECURITY_INFORMATION)
4383 control |= phead->control & SE_OWNER_DEFAULTED;
4384 if (selection & GROUP_SECURITY_INFORMATION)
4385 control |= phead->control & SE_GROUP_DEFAULTED;
4386 if (selection & DACL_SECURITY_INFORMATION)
4387 control |= phead->control
4388 & (SE_DACL_PRESENT
4389 | SE_DACL_DEFAULTED
4390 | SE_DACL_AUTO_INHERITED
4391 | SE_DACL_PROTECTED);
4392 if (selection & SACL_SECURITY_INFORMATION)
4393 control |= phead->control
4394 & (SE_SACL_PRESENT
4395 | SE_SACL_DEFAULTED
4396 | SE_SACL_AUTO_INHERITED
4397 | SE_SACL_PROTECTED);
4399 * copy header and feed new flags, even if no detailed data
4401 memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
4402 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
4403 pnhead->control = control;
4404 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4406 /* copy DACL if requested and available */
4407 if (selection & avail & DACL_SECURITY_INFORMATION) {
4408 pnhead->dacl = cpu_to_le32(pos);
4409 memcpy(&buf[pos],&attr[offdacl],daclsz);
4410 pos += daclsz;
4411 } else
4412 pnhead->dacl = const_cpu_to_le32(0);
4414 /* copy SACL if requested and available */
4415 if (selection & avail & SACL_SECURITY_INFORMATION) {
4416 pnhead->sacl = cpu_to_le32(pos);
4417 memcpy(&buf[pos],&attr[offsacl],saclsz);
4418 pos += saclsz;
4419 } else
4420 pnhead->sacl = const_cpu_to_le32(0);
4422 /* copy owner if requested and available */
4423 if (selection & avail & OWNER_SECURITY_INFORMATION) {
4424 pnhead->owner = cpu_to_le32(pos);
4425 memcpy(&buf[pos],&attr[offowner],usidsz);
4426 pos += usidsz;
4427 } else
4428 pnhead->owner = const_cpu_to_le32(0);
4430 /* copy group if requested and available */
4431 if (selection & avail & GROUP_SECURITY_INFORMATION) {
4432 pnhead->group = cpu_to_le32(pos);
4433 memcpy(&buf[pos],&attr[offgroup],gsidsz);
4434 pos += gsidsz;
4435 } else
4436 pnhead->group = const_cpu_to_le32(0);
4437 if (pos != size)
4438 ntfs_log_error("Error in security descriptor size\n");
4439 *psize = size;
4440 ok = TRUE;
4443 return (ok);
4447 * Merge a new security descriptor into the old one
4448 * and assign to designated file
4450 * Returns TRUE if successful
4453 static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
4454 const char *newattr, u32 selection, ntfs_inode *ni)
4456 const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
4457 const SECURITY_DESCRIPTOR_RELATIVE *newhead;
4458 SECURITY_DESCRIPTOR_RELATIVE *targhead;
4459 const ACL *pdacl;
4460 const ACL *psacl;
4461 const SID *powner;
4462 const SID *pgroup;
4463 int offdacl;
4464 int offsacl;
4465 int offowner;
4466 int offgroup;
4467 unsigned int size;
4468 le16 control;
4469 char *target;
4470 int pos;
4471 int oldattrsz;
4472 int newattrsz;
4473 BOOL ok;
4475 ok = FALSE; /* default return */
4476 oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
4477 newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
4478 oldattrsz = ntfs_attr_size(oldattr);
4479 newattrsz = ntfs_attr_size(newattr);
4480 target = (char*)ntfs_malloc(oldattrsz + newattrsz);
4481 if (target) {
4482 targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
4483 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4484 control = SE_SELF_RELATIVE;
4486 * copy new DACL if selected
4487 * or keep old DACL if any
4489 if ((selection & DACL_SECURITY_INFORMATION) ?
4490 newhead->dacl : oldhead->dacl) {
4491 if (selection & DACL_SECURITY_INFORMATION) {
4492 offdacl = le32_to_cpu(newhead->dacl);
4493 pdacl = (const ACL*)&newattr[offdacl];
4494 } else {
4495 offdacl = le32_to_cpu(oldhead->dacl);
4496 pdacl = (const ACL*)&oldattr[offdacl];
4498 size = le16_to_cpu(pdacl->size);
4499 memcpy(&target[pos], pdacl, size);
4500 targhead->dacl = cpu_to_le32(pos);
4501 pos += size;
4502 } else
4503 targhead->dacl = const_cpu_to_le32(0);
4504 if (selection & DACL_SECURITY_INFORMATION) {
4505 control |= newhead->control
4506 & (SE_DACL_PRESENT
4507 | SE_DACL_DEFAULTED
4508 | SE_DACL_PROTECTED);
4509 if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
4510 control |= SE_DACL_AUTO_INHERITED;
4511 } else
4512 control |= oldhead->control
4513 & (SE_DACL_PRESENT
4514 | SE_DACL_DEFAULTED
4515 | SE_DACL_AUTO_INHERITED
4516 | SE_DACL_PROTECTED);
4518 * copy new SACL if selected
4519 * or keep old SACL if any
4521 if ((selection & SACL_SECURITY_INFORMATION) ?
4522 newhead->sacl : oldhead->sacl) {
4523 if (selection & SACL_SECURITY_INFORMATION) {
4524 offsacl = le32_to_cpu(newhead->sacl);
4525 psacl = (const ACL*)&newattr[offsacl];
4526 } else {
4527 offsacl = le32_to_cpu(oldhead->sacl);
4528 psacl = (const ACL*)&oldattr[offsacl];
4530 size = le16_to_cpu(psacl->size);
4531 memcpy(&target[pos], psacl, size);
4532 targhead->sacl = cpu_to_le32(pos);
4533 pos += size;
4534 } else
4535 targhead->sacl = const_cpu_to_le32(0);
4536 if (selection & SACL_SECURITY_INFORMATION) {
4537 control |= newhead->control
4538 & (SE_SACL_PRESENT
4539 | SE_SACL_DEFAULTED
4540 | SE_SACL_PROTECTED);
4541 if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
4542 control |= SE_SACL_AUTO_INHERITED;
4543 } else
4544 control |= oldhead->control
4545 & (SE_SACL_PRESENT
4546 | SE_SACL_DEFAULTED
4547 | SE_SACL_AUTO_INHERITED
4548 | SE_SACL_PROTECTED);
4550 * copy new OWNER if selected
4551 * or keep old OWNER if any
4553 if ((selection & OWNER_SECURITY_INFORMATION) ?
4554 newhead->owner : oldhead->owner) {
4555 if (selection & OWNER_SECURITY_INFORMATION) {
4556 offowner = le32_to_cpu(newhead->owner);
4557 powner = (const SID*)&newattr[offowner];
4558 } else {
4559 offowner = le32_to_cpu(oldhead->owner);
4560 powner = (const SID*)&oldattr[offowner];
4562 size = ntfs_sid_size(powner);
4563 memcpy(&target[pos], powner, size);
4564 targhead->owner = cpu_to_le32(pos);
4565 pos += size;
4566 } else
4567 targhead->owner = const_cpu_to_le32(0);
4568 if (selection & OWNER_SECURITY_INFORMATION)
4569 control |= newhead->control & SE_OWNER_DEFAULTED;
4570 else
4571 control |= oldhead->control & SE_OWNER_DEFAULTED;
4573 * copy new GROUP if selected
4574 * or keep old GROUP if any
4576 if ((selection & GROUP_SECURITY_INFORMATION) ?
4577 newhead->group : oldhead->group) {
4578 if (selection & GROUP_SECURITY_INFORMATION) {
4579 offgroup = le32_to_cpu(newhead->group);
4580 pgroup = (const SID*)&newattr[offgroup];
4581 control |= newhead->control
4582 & SE_GROUP_DEFAULTED;
4583 } else {
4584 offgroup = le32_to_cpu(oldhead->group);
4585 pgroup = (const SID*)&oldattr[offgroup];
4586 control |= oldhead->control
4587 & SE_GROUP_DEFAULTED;
4589 size = ntfs_sid_size(pgroup);
4590 memcpy(&target[pos], pgroup, size);
4591 targhead->group = cpu_to_le32(pos);
4592 pos += size;
4593 } else
4594 targhead->group = const_cpu_to_le32(0);
4595 if (selection & GROUP_SECURITY_INFORMATION)
4596 control |= newhead->control & SE_GROUP_DEFAULTED;
4597 else
4598 control |= oldhead->control & SE_GROUP_DEFAULTED;
4599 targhead->revision = SECURITY_DESCRIPTOR_REVISION;
4600 targhead->alignment = 0;
4601 targhead->control = control;
4602 ok = !update_secur_descr(vol, target, ni);
4603 free(target);
4605 return (ok);
4609 * Return the security descriptor of a file
4610 * This is intended to be similar to GetFileSecurity() from Win32
4611 * in order to facilitate the development of portable tools
4613 * returns zero if unsuccessful (following Win32 conventions)
4614 * -1 if no securid
4615 * the securid if any
4617 * The Win32 API is :
4619 * BOOL WINAPI GetFileSecurity(
4620 * __in LPCTSTR lpFileName,
4621 * __in SECURITY_INFORMATION RequestedInformation,
4622 * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor,
4623 * __in DWORD nLength,
4624 * __out LPDWORD lpnLengthNeeded
4625 * );
4629 int ntfs_get_file_security(struct SECURITY_API *scapi,
4630 const char *path, u32 selection,
4631 char *buf, u32 buflen, u32 *psize)
4633 ntfs_inode *ni;
4634 char *attr;
4635 int res;
4637 res = 0; /* default return */
4638 if (scapi && (scapi->magic == MAGIC_API)) {
4639 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4640 if (ni) {
4641 attr = getsecurityattr(scapi->security.vol, ni);
4642 if (attr) {
4643 if (feedsecurityattr(attr,selection,
4644 buf,buflen,psize)) {
4645 if (test_nino_flag(ni, v3_Extensions)
4646 && ni->security_id)
4647 res = le32_to_cpu(
4648 ni->security_id);
4649 else
4650 res = -1;
4652 free(attr);
4654 ntfs_inode_close(ni);
4655 } else
4656 errno = ENOENT;
4657 if (!res) *psize = 0;
4658 } else
4659 errno = EINVAL; /* do not clear *psize */
4660 return (res);
4665 * Set the security descriptor of a file or directory
4666 * This is intended to be similar to SetFileSecurity() from Win32
4667 * in order to facilitate the development of portable tools
4669 * returns zero if unsuccessful (following Win32 conventions)
4670 * -1 if no securid
4671 * the securid if any
4673 * The Win32 API is :
4675 * BOOL WINAPI SetFileSecurity(
4676 * __in LPCTSTR lpFileName,
4677 * __in SECURITY_INFORMATION SecurityInformation,
4678 * __in PSECURITY_DESCRIPTOR pSecurityDescriptor
4679 * );
4682 int ntfs_set_file_security(struct SECURITY_API *scapi,
4683 const char *path, u32 selection, const char *attr)
4685 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4686 ntfs_inode *ni;
4687 int attrsz;
4688 BOOL missing;
4689 char *oldattr;
4690 int res;
4692 res = 0; /* default return */
4693 if (scapi && (scapi->magic == MAGIC_API) && attr) {
4694 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4695 attrsz = ntfs_attr_size(attr);
4696 /* if selected, owner and group must be present or defaulted */
4697 missing = ((selection & OWNER_SECURITY_INFORMATION)
4698 && !phead->owner
4699 && !(phead->control & SE_OWNER_DEFAULTED))
4700 || ((selection & GROUP_SECURITY_INFORMATION)
4701 && !phead->group
4702 && !(phead->control & SE_GROUP_DEFAULTED));
4703 if (!missing
4704 && (phead->control & SE_SELF_RELATIVE)
4705 && ntfs_valid_descr(attr, attrsz)) {
4706 ni = ntfs_pathname_to_inode(scapi->security.vol,
4707 NULL, path);
4708 if (ni) {
4709 oldattr = getsecurityattr(scapi->security.vol,
4710 ni);
4711 if (oldattr) {
4712 if (mergesecurityattr(
4713 scapi->security.vol,
4714 oldattr, attr,
4715 selection, ni)) {
4716 if (test_nino_flag(ni,
4717 v3_Extensions))
4718 res = le32_to_cpu(
4719 ni->security_id);
4720 else
4721 res = -1;
4723 free(oldattr);
4725 ntfs_inode_close(ni);
4727 } else
4728 errno = EINVAL;
4729 } else
4730 errno = EINVAL;
4731 return (res);
4736 * Return the attributes of a file
4737 * This is intended to be similar to GetFileAttributes() from Win32
4738 * in order to facilitate the development of portable tools
4740 * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES)
4742 * The Win32 API is :
4744 * DWORD WINAPI GetFileAttributes(
4745 * __in LPCTSTR lpFileName
4746 * );
4749 int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
4751 ntfs_inode *ni;
4752 s32 attrib;
4754 attrib = -1; /* default return */
4755 if (scapi && (scapi->magic == MAGIC_API) && path) {
4756 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4757 if (ni) {
4758 attrib = le32_to_cpu(ni->flags);
4759 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4760 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4761 else
4762 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4763 if (!attrib)
4764 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4766 ntfs_inode_close(ni);
4767 } else
4768 errno = ENOENT;
4769 } else
4770 errno = EINVAL; /* do not clear *psize */
4771 return (attrib);
4776 * Set attributes to a file or directory
4777 * This is intended to be similar to SetFileAttributes() from Win32
4778 * in order to facilitate the development of portable tools
4780 * Only a few flags can be set (same list as Win32)
4782 * returns zero if unsuccessful (following Win32 conventions)
4783 * nonzero if successful
4785 * The Win32 API is :
4787 * BOOL WINAPI SetFileAttributes(
4788 * __in LPCTSTR lpFileName,
4789 * __in DWORD dwFileAttributes
4790 * );
4793 BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
4794 const char *path, s32 attrib)
4796 ntfs_inode *ni;
4797 le32 settable;
4798 ATTR_FLAGS dirflags;
4799 int res;
4801 res = 0; /* default return */
4802 if (scapi && (scapi->magic == MAGIC_API) && path) {
4803 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4804 if (ni) {
4805 settable = FILE_ATTR_SETTABLE;
4806 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4808 * Accept changing compression for a directory
4809 * and set index root accordingly
4811 settable |= FILE_ATTR_COMPRESSED;
4812 if ((ni->flags ^ cpu_to_le32(attrib))
4813 & FILE_ATTR_COMPRESSED) {
4814 if (ni->flags & FILE_ATTR_COMPRESSED)
4815 dirflags = const_cpu_to_le16(0);
4816 else
4817 dirflags = ATTR_IS_COMPRESSED;
4818 res = ntfs_attr_set_flags(ni,
4819 AT_INDEX_ROOT,
4820 NTFS_INDEX_I30, 4,
4821 dirflags,
4822 ATTR_COMPRESSION_MASK);
4825 if (!res) {
4826 ni->flags = (ni->flags & ~settable)
4827 | (cpu_to_le32(attrib) & settable);
4828 NInoSetDirty(ni);
4830 if (!ntfs_inode_close(ni))
4831 res = -1;
4832 } else
4833 errno = ENOENT;
4835 return (res);
4839 BOOL ntfs_read_directory(struct SECURITY_API *scapi,
4840 const char *path, ntfs_filldir_t callback, void *context)
4842 ntfs_inode *ni;
4843 BOOL ok;
4844 s64 pos;
4846 ok = FALSE; /* default return */
4847 if (scapi && (scapi->magic == MAGIC_API) && callback) {
4848 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4849 if (ni) {
4850 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4851 pos = 0;
4852 ntfs_readdir(ni,&pos,context,callback);
4853 ok = !ntfs_inode_close(ni);
4854 } else {
4855 ntfs_inode_close(ni);
4856 errno = ENOTDIR;
4858 } else
4859 errno = ENOENT;
4860 } else
4861 errno = EINVAL; /* do not clear *psize */
4862 return (ok);
4866 * read $SDS (for auditing security data)
4868 * Returns the number or read bytes, or -1 if there is an error
4871 int ntfs_read_sds(struct SECURITY_API *scapi,
4872 char *buf, u32 size, u32 offset)
4874 int got;
4876 got = -1; /* default return */
4877 if (scapi && (scapi->magic == MAGIC_API)) {
4878 if (scapi->security.vol->secure_ni)
4879 got = ntfs_local_read(scapi->security.vol->secure_ni,
4880 STREAM_SDS, 4, buf, size, offset);
4881 else
4882 errno = EOPNOTSUPP;
4883 } else
4884 errno = EINVAL;
4885 return (got);
4889 * read $SII (for auditing security data)
4891 * Returns next entry, or NULL if there is an error
4894 INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
4895 INDEX_ENTRY *entry)
4897 SII_INDEX_KEY key;
4898 INDEX_ENTRY *ret;
4899 BOOL found;
4900 ntfs_index_context *xsii;
4902 ret = (INDEX_ENTRY*)NULL; /* default return */
4903 if (scapi && (scapi->magic == MAGIC_API)) {
4904 xsii = scapi->security.vol->secure_xsii;
4905 if (xsii) {
4906 if (!entry) {
4907 key.security_id = const_cpu_to_le32(0);
4908 found = !ntfs_index_lookup((char*)&key,
4909 sizeof(SII_INDEX_KEY), xsii);
4910 /* not supposed to find */
4911 if (!found && (errno == ENOENT))
4912 ret = xsii->entry;
4913 } else
4914 ret = ntfs_index_next(entry,xsii);
4915 if (!ret)
4916 errno = ENODATA;
4917 } else
4918 errno = EOPNOTSUPP;
4919 } else
4920 errno = EINVAL;
4921 return (ret);
4925 * read $SDH (for auditing security data)
4927 * Returns next entry, or NULL if there is an error
4930 INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
4931 INDEX_ENTRY *entry)
4933 SDH_INDEX_KEY key;
4934 INDEX_ENTRY *ret;
4935 BOOL found;
4936 ntfs_index_context *xsdh;
4938 ret = (INDEX_ENTRY*)NULL; /* default return */
4939 if (scapi && (scapi->magic == MAGIC_API)) {
4940 xsdh = scapi->security.vol->secure_xsdh;
4941 if (xsdh) {
4942 if (!entry) {
4943 key.hash = const_cpu_to_le32(0);
4944 key.security_id = const_cpu_to_le32(0);
4945 found = !ntfs_index_lookup((char*)&key,
4946 sizeof(SDH_INDEX_KEY), xsdh);
4947 /* not supposed to find */
4948 if (!found && (errno == ENOENT))
4949 ret = xsdh->entry;
4950 } else
4951 ret = ntfs_index_next(entry,xsdh);
4952 if (!ret)
4953 errno = ENODATA;
4954 } else errno = ENOTSUP;
4955 } else
4956 errno = EINVAL;
4957 return (ret);
4961 * Get the mapped user SID
4962 * A buffer of 40 bytes has to be supplied
4964 * returns the size of the SID, or zero and errno set if not found
4967 int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
4969 const SID *usid;
4970 BIGSID defusid;
4971 int size;
4973 size = 0;
4974 if (scapi && (scapi->magic == MAGIC_API)) {
4975 usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
4976 if (usid) {
4977 size = ntfs_sid_size(usid);
4978 memcpy(buf,usid,size);
4979 } else
4980 errno = ENODATA;
4981 } else
4982 errno = EINVAL;
4983 return (size);
4987 * Get the mapped group SID
4988 * A buffer of 40 bytes has to be supplied
4990 * returns the size of the SID, or zero and errno set if not found
4993 int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
4995 const SID *gsid;
4996 BIGSID defgsid;
4997 int size;
4999 size = 0;
5000 if (scapi && (scapi->magic == MAGIC_API)) {
5001 gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
5002 if (gsid) {
5003 size = ntfs_sid_size(gsid);
5004 memcpy(buf,gsid,size);
5005 } else
5006 errno = ENODATA;
5007 } else
5008 errno = EINVAL;
5009 return (size);
5013 * Get the user mapped to a SID
5015 * returns the uid, or -1 if not found
5018 int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
5020 int uid;
5022 uid = -1;
5023 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
5024 if (ntfs_same_sid(usid,adminsid))
5025 uid = 0;
5026 else {
5027 uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
5028 if (!uid) {
5029 uid = -1;
5030 errno = ENODATA;
5033 } else
5034 errno = EINVAL;
5035 return (uid);
5039 * Get the group mapped to a SID
5041 * returns the uid, or -1 if not found
5044 int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
5046 int gid;
5048 gid = -1;
5049 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
5050 if (ntfs_same_sid(gsid,adminsid))
5051 gid = 0;
5052 else {
5053 gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
5054 if (!gid) {
5055 gid = -1;
5056 errno = ENODATA;
5059 } else
5060 errno = EINVAL;
5061 return (gid);
5065 * Initializations before calling ntfs_get_file_security()
5066 * ntfs_set_file_security() and ntfs_read_directory()
5068 * Only allowed for root
5070 * Returns an (obscured) struct SECURITY_API* needed for further calls
5071 * NULL if not root (EPERM) or device is mounted (EBUSY)
5074 struct SECURITY_API *ntfs_initialize_file_security(const char *device,
5075 int flags)
5077 ntfs_volume *vol;
5078 unsigned long mntflag;
5079 int mnt;
5080 struct SECURITY_API *scapi;
5081 struct SECURITY_CONTEXT *scx;
5083 scapi = (struct SECURITY_API*)NULL;
5084 mnt = ntfs_check_if_mounted(device, &mntflag);
5085 if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
5086 vol = ntfs_mount(device, flags);
5087 if (vol) {
5088 scapi = (struct SECURITY_API*)
5089 ntfs_malloc(sizeof(struct SECURITY_API));
5090 if (!ntfs_volume_get_free_space(vol)
5091 && scapi) {
5092 scapi->magic = MAGIC_API;
5093 scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
5094 scx = &scapi->security;
5095 scx->vol = vol;
5096 scx->uid = getuid();
5097 scx->gid = getgid();
5098 scx->pseccache = &scapi->seccache;
5099 scx->vol->secure_flags = 0;
5100 /* accept no mapping and no $Secure */
5101 ntfs_build_mapping(scx,(const char*)NULL);
5102 ntfs_open_secure(vol);
5103 } else {
5104 if (scapi)
5105 free(scapi);
5106 else
5107 errno = ENOMEM;
5108 mnt = ntfs_umount(vol,FALSE);
5109 scapi = (struct SECURITY_API*)NULL;
5112 } else
5113 if (getuid())
5114 errno = EPERM;
5115 else
5116 errno = EBUSY;
5117 return (scapi);
5121 * Leaving after ntfs_initialize_file_security()
5123 * Returns FALSE if FAILED
5126 BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
5128 int ok;
5129 ntfs_volume *vol;
5131 ok = FALSE;
5132 if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
5133 vol = scapi->security.vol;
5134 ntfs_close_secure(&scapi->security);
5135 free(scapi);
5136 if (!ntfs_umount(vol, 0))
5137 ok = TRUE;
5139 return (ok);