dnscrypto-proxy: Update to release 1.3.0
[tomato.git] / release / src / router / ntfs-3g / libntfs-3g / security.c
blob57e7331e2a71cf6d9390bf364ac39c73d31d4488
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-2012 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 "compat.h"
56 #include "param.h"
57 #include "types.h"
58 #include "layout.h"
59 #include "attrib.h"
60 #include "index.h"
61 #include "dir.h"
62 #include "bitmap.h"
63 #include "security.h"
64 #include "acls.h"
65 #include "cache.h"
66 #include "misc.h"
69 * JPA NTFS constants or structs
70 * should be moved to layout.h
73 #define ALIGN_SDS_BLOCK 0x40000 /* Alignment for a $SDS block */
74 #define ALIGN_SDS_ENTRY 16 /* Alignment for a $SDS entry */
75 #define STUFFSZ 0x4000 /* unitary stuffing size for $SDS */
76 #define FIRST_SECURITY_ID 0x100 /* Lowest security id */
78 /* Mask for attributes which can be forced */
79 #define FILE_ATTR_SETTABLE ( FILE_ATTR_READONLY \
80 | FILE_ATTR_HIDDEN \
81 | FILE_ATTR_SYSTEM \
82 | FILE_ATTR_ARCHIVE \
83 | FILE_ATTR_TEMPORARY \
84 | FILE_ATTR_OFFLINE \
85 | FILE_ATTR_NOT_CONTENT_INDEXED )
87 struct SII { /* this is an image of an $SII index entry */
88 le16 offs;
89 le16 size;
90 le32 fill1;
91 le16 indexsz;
92 le16 indexksz;
93 le16 flags;
94 le16 fill2;
95 le32 keysecurid;
97 /* did not find official description for the following */
98 le32 hash;
99 le32 securid;
100 le32 dataoffsl; /* documented as badly aligned */
101 le32 dataoffsh;
102 le32 datasize;
105 struct SDH { /* this is an image of an $SDH index entry */
106 le16 offs;
107 le16 size;
108 le32 fill1;
109 le16 indexsz;
110 le16 indexksz;
111 le16 flags;
112 le16 fill2;
113 le32 keyhash;
114 le32 keysecurid;
116 /* did not find official description for the following */
117 le32 hash;
118 le32 securid;
119 le32 dataoffsl;
120 le32 dataoffsh;
121 le32 datasize;
122 le32 fill3;
126 * A few useful constants
129 static ntfschar sii_stream[] = { const_cpu_to_le16('$'),
130 const_cpu_to_le16('S'),
131 const_cpu_to_le16('I'),
132 const_cpu_to_le16('I'),
133 const_cpu_to_le16(0) };
134 static ntfschar sdh_stream[] = { const_cpu_to_le16('$'),
135 const_cpu_to_le16('S'),
136 const_cpu_to_le16('D'),
137 const_cpu_to_le16('H'),
138 const_cpu_to_le16(0) };
141 * null SID (S-1-0-0)
144 extern const SID *nullsid;
147 * The zero GUID.
150 static const GUID __zero_guid = { const_cpu_to_le32(0), const_cpu_to_le16(0),
151 const_cpu_to_le16(0), { 0, 0, 0, 0, 0, 0, 0, 0 } };
152 static const GUID *const zero_guid = &__zero_guid;
155 * ntfs_guid_is_zero - check if a GUID is zero
156 * @guid: [IN] guid to check
158 * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
159 * and FALSE otherwise.
161 BOOL ntfs_guid_is_zero(const GUID *guid)
163 return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
167 * ntfs_guid_to_mbs - convert a GUID to a multi byte string
168 * @guid: [IN] guid to convert
169 * @guid_str: [OUT] string in which to return the GUID (optional)
171 * Convert the GUID pointed to by @guid to a multi byte string of the form
172 * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL)
173 * needs to be able to store at least 37 bytes.
175 * If @guid_str is not NULL it will contain the converted GUID on return. If
176 * it is NULL a string will be allocated and this will be returned. The caller
177 * is responsible for free()ing the string in that case.
179 * On success return the converted string and on failure return NULL with errno
180 * set to the error code.
182 char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
184 char *_guid_str;
185 int res;
187 if (!guid) {
188 errno = EINVAL;
189 return NULL;
191 _guid_str = guid_str;
192 if (!_guid_str) {
193 _guid_str = (char*)ntfs_malloc(37);
194 if (!_guid_str)
195 return _guid_str;
197 res = snprintf(_guid_str, 37,
198 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
199 (unsigned int)le32_to_cpu(guid->data1),
200 le16_to_cpu(guid->data2), le16_to_cpu(guid->data3),
201 guid->data4[0], guid->data4[1],
202 guid->data4[2], guid->data4[3], guid->data4[4],
203 guid->data4[5], guid->data4[6], guid->data4[7]);
204 if (res == 36)
205 return _guid_str;
206 if (!guid_str)
207 free(_guid_str);
208 errno = EINVAL;
209 return NULL;
213 * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
214 * @sid: [IN] SID for which to determine the maximum string size
216 * Determine the maximum multi byte string size in bytes which is needed to
217 * store the standard textual representation of the SID pointed to by @sid.
218 * See ntfs_sid_to_mbs(), below.
220 * On success return the maximum number of bytes needed to store the multi byte
221 * string and on failure return -1 with errno set to the error code.
223 int ntfs_sid_to_mbs_size(const SID *sid)
225 int size, i;
227 if (!ntfs_sid_is_valid(sid)) {
228 errno = EINVAL;
229 return -1;
231 /* Start with "S-". */
232 size = 2;
234 * Add the SID_REVISION. Hopefully the compiler will optimize this
235 * away as SID_REVISION is a constant.
237 for (i = SID_REVISION; i > 0; i /= 10)
238 size++;
239 /* Add the "-". */
240 size++;
242 * Add the identifier authority. If it needs to be in decimal, the
243 * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be
244 * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
246 if (!sid->identifier_authority.high_part)
247 size += 10;
248 else
249 size += 14;
251 * Finally, add the sub authorities. For each we have a "-" followed
252 * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
254 size += (1 + 10) * sid->sub_authority_count;
255 /* We need the zero byte at the end, too. */
256 size++;
257 return size * sizeof(char);
261 * ntfs_sid_to_mbs - convert a SID to a multi byte string
262 * @sid: [IN] SID to convert
263 * @sid_str: [OUT] string in which to return the SID (optional)
264 * @sid_str_size: [IN] size in bytes of @sid_str
266 * Convert the SID pointed to by @sid to its standard textual representation.
267 * @sid_str (if not NULL) needs to be able to store at least
268 * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of
269 * @sid_str if @sid_str is not NULL.
271 * The standard textual representation of the SID is of the form:
272 * S-R-I-S-S...
273 * Where:
274 * - The first "S" is the literal character 'S' identifying the following
275 * digits as a SID.
276 * - R is the revision level of the SID expressed as a sequence of digits
277 * in decimal.
278 * - I is the 48-bit identifier_authority, expressed as digits in decimal,
279 * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
280 * - S... is one or more sub_authority values, expressed as digits in
281 * decimal.
283 * If @sid_str is not NULL it will contain the converted SUID on return. If it
284 * is NULL a string will be allocated and this will be returned. The caller is
285 * responsible for free()ing the string in that case.
287 * On success return the converted string and on failure return NULL with errno
288 * set to the error code.
290 char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
292 u64 u;
293 le32 leauth;
294 char *s;
295 int i, j, cnt;
298 * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
299 * check @sid, too. 8 is the minimum SID string size.
301 if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) {
302 errno = EINVAL;
303 return NULL;
305 /* Allocate string if not provided. */
306 if (!sid_str) {
307 cnt = ntfs_sid_to_mbs_size(sid);
308 if (cnt < 0)
309 return NULL;
310 s = (char*)ntfs_malloc(cnt);
311 if (!s)
312 return s;
313 sid_str = s;
314 /* So we know we allocated it. */
315 sid_str_size = 0;
316 } else {
317 s = sid_str;
318 cnt = sid_str_size;
320 /* Start with "S-R-". */
321 i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
322 if (i < 0 || i >= cnt)
323 goto err_out;
324 s += i;
325 cnt -= i;
326 /* Add the identifier authority. */
327 for (u = i = 0, j = 40; i < 6; i++, j -= 8)
328 u += (u64)sid->identifier_authority.value[i] << j;
329 if (!sid->identifier_authority.high_part)
330 i = snprintf(s, cnt, "%lu", (unsigned long)u);
331 else
332 i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
333 if (i < 0 || i >= cnt)
334 goto err_out;
335 s += i;
336 cnt -= i;
337 /* Finally, add the sub authorities. */
338 for (j = 0; j < sid->sub_authority_count; j++) {
339 leauth = sid->sub_authority[j];
340 i = snprintf(s, cnt, "-%u", (unsigned int)
341 le32_to_cpu(leauth));
342 if (i < 0 || i >= cnt)
343 goto err_out;
344 s += i;
345 cnt -= i;
347 return sid_str;
348 err_out:
349 if (i >= cnt)
350 i = EMSGSIZE;
351 else
352 i = errno;
353 if (!sid_str_size)
354 free(sid_str);
355 errno = i;
356 return NULL;
360 * ntfs_generate_guid - generatates a random current guid.
361 * @guid: [OUT] pointer to a GUID struct to hold the generated guid.
363 * perhaps not a very good random number generator though...
365 void ntfs_generate_guid(GUID *guid)
367 unsigned int i;
368 u8 *p = (u8 *)guid;
370 for (i = 0; i < sizeof(GUID); i++) {
371 p[i] = (u8)(random() & 0xFF);
372 if (i == 7)
373 p[7] = (p[7] & 0x0F) | 0x40;
374 if (i == 8)
375 p[8] = (p[8] & 0x3F) | 0x80;
380 * ntfs_security_hash - calculate the hash of a security descriptor
381 * @sd: self-relative security descriptor whose hash to calculate
382 * @length: size in bytes of the security descritor @sd
384 * Calculate the hash of the self-relative security descriptor @sd of length
385 * @length bytes.
387 * This hash is used in the $Secure system file as the primary key for the $SDH
388 * index and is also stored in the header of each security descriptor in the
389 * $SDS data stream as well as in the index data of both the $SII and $SDH
390 * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER
391 * structure.
393 * Return the calculated security hash in little endian.
395 le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
397 const le32 *pos = (const le32*)sd;
398 const le32 *end = pos + (len >> 2);
399 u32 hash = 0;
401 while (pos < end) {
402 hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
403 pos++;
405 return cpu_to_le32(hash);
409 * Get the first entry of current index block
410 * cut and pasted form ntfs_ie_get_first() in index.c
413 static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
415 return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
419 * Stuff a 256KB block into $SDS before writing descriptors
420 * into the block.
422 * This prevents $SDS from being automatically declared as sparse
423 * when the second copy of the first security descriptor is written
424 * 256KB further ahead.
426 * Having $SDS declared as a sparse file is not wrong by itself
427 * and chkdsk leaves it as a sparse file. It does however complain
428 * and add a sparse flag (0x0200) into field file_attributes of
429 * STANDARD_INFORMATION of $Secure. This probably means that a
430 * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
431 * files (FILE_ATTR_SPARSE_FILE).
433 * Windows normally does not convert to sparse attribute or sparse
434 * file. Stuffing is just a way to get to the same result.
437 static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
439 int res;
440 int written;
441 unsigned long total;
442 char *stuff;
444 res = 0;
445 total = 0;
446 stuff = (char*)ntfs_malloc(STUFFSZ);
447 if (stuff) {
448 memset(stuff, 0, STUFFSZ);
449 do {
450 written = ntfs_attr_data_write(vol->secure_ni,
451 STREAM_SDS, 4, stuff, STUFFSZ, offs);
452 if (written == STUFFSZ) {
453 total += STUFFSZ;
454 offs += STUFFSZ;
455 } else {
456 errno = ENOSPC;
457 res = -1;
459 } while (!res && (total < ALIGN_SDS_BLOCK));
460 free(stuff);
461 } else {
462 errno = ENOMEM;
463 res = -1;
465 return (res);
469 * Enter a new security descriptor into $Secure (data only)
470 * it has to be written twice with an offset of 256KB
472 * Should only be called by entersecurityattr() to ensure consistency
474 * Returns zero if sucessful
477 static int entersecurity_data(ntfs_volume *vol,
478 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
479 le32 hash, le32 keyid, off_t offs, int gap)
481 int res;
482 int written1;
483 int written2;
484 char *fullattr;
485 int fullsz;
486 SECURITY_DESCRIPTOR_HEADER *phsds;
488 res = -1;
489 fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
490 fullattr = (char*)ntfs_malloc(fullsz);
491 if (fullattr) {
493 * Clear the gap from previous descriptor
494 * this could be useful for appending the second
495 * copy to the end of file. When creating a new
496 * 256K block, the gap is cleared while writing
497 * the first copy
499 if (gap)
500 memset(fullattr,0,gap);
501 memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
502 attr,attrsz);
503 phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
504 phsds->hash = hash;
505 phsds->security_id = keyid;
506 phsds->offset = cpu_to_le64(offs);
507 phsds->length = cpu_to_le32(fullsz - gap);
508 written1 = ntfs_attr_data_write(vol->secure_ni,
509 STREAM_SDS, 4, fullattr, fullsz,
510 offs - gap);
511 written2 = ntfs_attr_data_write(vol->secure_ni,
512 STREAM_SDS, 4, fullattr, fullsz,
513 offs - gap + ALIGN_SDS_BLOCK);
514 if ((written1 == fullsz)
515 && (written2 == written1))
516 res = 0;
517 else
518 errno = ENOSPC;
519 free(fullattr);
520 } else
521 errno = ENOMEM;
522 return (res);
526 * Enter a new security descriptor in $Secure (indexes only)
528 * Should only be called by entersecurityattr() to ensure consistency
530 * Returns zero if sucessful
533 static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
534 le32 hash, le32 keyid, off_t offs)
536 union {
537 struct {
538 le32 dataoffsl;
539 le32 dataoffsh;
540 } parts;
541 le64 all;
542 } realign;
543 int res;
544 ntfs_index_context *xsii;
545 ntfs_index_context *xsdh;
546 struct SII newsii;
547 struct SDH newsdh;
549 res = -1;
550 /* enter a new $SII record */
552 xsii = vol->secure_xsii;
553 ntfs_index_ctx_reinit(xsii);
554 newsii.offs = const_cpu_to_le16(20);
555 newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
556 newsii.fill1 = const_cpu_to_le32(0);
557 newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
558 newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
559 newsii.flags = const_cpu_to_le16(0);
560 newsii.fill2 = const_cpu_to_le16(0);
561 newsii.keysecurid = keyid;
562 newsii.hash = hash;
563 newsii.securid = keyid;
564 realign.all = cpu_to_le64(offs);
565 newsii.dataoffsh = realign.parts.dataoffsh;
566 newsii.dataoffsl = realign.parts.dataoffsl;
567 newsii.datasize = cpu_to_le32(attrsz
568 + sizeof(SECURITY_DESCRIPTOR_HEADER));
569 if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
571 /* enter a new $SDH record */
573 xsdh = vol->secure_xsdh;
574 ntfs_index_ctx_reinit(xsdh);
575 newsdh.offs = const_cpu_to_le16(24);
576 newsdh.size = const_cpu_to_le16(
577 sizeof(SECURITY_DESCRIPTOR_HEADER));
578 newsdh.fill1 = const_cpu_to_le32(0);
579 newsdh.indexsz = const_cpu_to_le16(
580 sizeof(struct SDH));
581 newsdh.indexksz = const_cpu_to_le16(
582 sizeof(SDH_INDEX_KEY));
583 newsdh.flags = const_cpu_to_le16(0);
584 newsdh.fill2 = const_cpu_to_le16(0);
585 newsdh.keyhash = hash;
586 newsdh.keysecurid = keyid;
587 newsdh.hash = hash;
588 newsdh.securid = keyid;
589 newsdh.dataoffsh = realign.parts.dataoffsh;
590 newsdh.dataoffsl = realign.parts.dataoffsl;
591 newsdh.datasize = cpu_to_le32(attrsz
592 + sizeof(SECURITY_DESCRIPTOR_HEADER));
593 /* special filler value, Windows generally */
594 /* fills with 0x00490049, sometimes with zero */
595 newsdh.fill3 = const_cpu_to_le32(0x00490049);
596 if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
597 res = 0;
599 return (res);
603 * Enter a new security descriptor in $Secure (data and indexes)
604 * Returns id of entry, or zero if there is a problem.
605 * (should not be called for NTFS version < 3.0)
607 * important : calls have to be serialized, however no locking is
608 * needed while fuse is not multithreaded
611 static le32 entersecurityattr(ntfs_volume *vol,
612 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
613 le32 hash)
615 union {
616 struct {
617 le32 dataoffsl;
618 le32 dataoffsh;
619 } parts;
620 le64 all;
621 } realign;
622 le32 securid;
623 le32 keyid;
624 u32 newkey;
625 off_t offs;
626 int gap;
627 int size;
628 BOOL found;
629 struct SII *psii;
630 INDEX_ENTRY *entry;
631 INDEX_ENTRY *next;
632 ntfs_index_context *xsii;
633 int retries;
634 ntfs_attr *na;
635 int olderrno;
637 /* find the first available securid beyond the last key */
638 /* in $Secure:$SII. This also determines the first */
639 /* available location in $Secure:$SDS, as this stream */
640 /* is always appended to and the id's are allocated */
641 /* in sequence */
643 securid = const_cpu_to_le32(0);
644 xsii = vol->secure_xsii;
645 ntfs_index_ctx_reinit(xsii);
646 offs = size = 0;
647 keyid = const_cpu_to_le32(-1);
648 olderrno = errno;
649 found = !ntfs_index_lookup((char*)&keyid,
650 sizeof(SII_INDEX_KEY), xsii);
651 if (!found && (errno != ENOENT)) {
652 ntfs_log_perror("Inconsistency in index $SII");
653 psii = (struct SII*)NULL;
654 } else {
655 /* restore errno to avoid misinterpretation */
656 errno = olderrno;
657 entry = xsii->entry;
658 psii = (struct SII*)xsii->entry;
660 if (psii) {
662 * Get last entry in block, but must get first one
663 * one first, as we should already be beyond the
664 * last one. For some reason the search for the last
665 * entry sometimes does not return the last block...
666 * we assume this can only happen in root block
668 if (xsii->is_in_root)
669 entry = ntfs_ie_get_first
670 ((INDEX_HEADER*)&xsii->ir->index);
671 else
672 entry = ntfs_ie_get_first
673 ((INDEX_HEADER*)&xsii->ib->index);
675 * All index blocks should be at least half full
676 * so there always is a last entry but one,
677 * except when creating the first entry in index root.
678 * This was however found not to be true : chkdsk
679 * sometimes deletes all the (unused) keys in the last
680 * index block without rebalancing the tree.
681 * When this happens, a new search is restarted from
682 * the smallest key.
684 keyid = const_cpu_to_le32(0);
685 retries = 0;
686 while (entry) {
687 next = ntfs_index_next(entry,xsii);
688 if (next) {
689 psii = (struct SII*)next;
690 /* save last key and */
691 /* available position */
692 keyid = psii->keysecurid;
693 realign.parts.dataoffsh
694 = psii->dataoffsh;
695 realign.parts.dataoffsl
696 = psii->dataoffsl;
697 offs = le64_to_cpu(realign.all);
698 size = le32_to_cpu(psii->datasize);
700 entry = next;
701 if (!entry && !keyid && !retries) {
702 /* search failed, retry from smallest key */
703 ntfs_index_ctx_reinit(xsii);
704 found = !ntfs_index_lookup((char*)&keyid,
705 sizeof(SII_INDEX_KEY), xsii);
706 if (!found && (errno != ENOENT)) {
707 ntfs_log_perror("Index $SII is broken");
708 } else {
709 /* restore errno */
710 errno = olderrno;
711 entry = xsii->entry;
713 retries++;
717 if (!keyid) {
719 * could not find any entry, before creating the first
720 * entry, make a double check by making sure size of $SII
721 * is less than needed for one entry
723 securid = const_cpu_to_le32(0);
724 na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
725 if (na) {
726 if ((size_t)na->data_size < sizeof(struct SII)) {
727 ntfs_log_error("Creating the first security_id\n");
728 securid = const_cpu_to_le32(FIRST_SECURITY_ID);
730 ntfs_attr_close(na);
732 if (!securid) {
733 ntfs_log_error("Error creating a security_id\n");
734 errno = EIO;
736 } else {
737 newkey = le32_to_cpu(keyid) + 1;
738 securid = cpu_to_le32(newkey);
741 * The security attr has to be written twice 256KB
742 * apart. This implies that offsets like
743 * 0x40000*odd_integer must be left available for
744 * the second copy. So align to next block when
745 * the last byte overflows on a wrong block.
748 if (securid) {
749 gap = (-size) & (ALIGN_SDS_ENTRY - 1);
750 offs += gap + size;
751 if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
752 & ALIGN_SDS_BLOCK) {
753 offs = ((offs + attrsz
754 + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
755 | (ALIGN_SDS_BLOCK - 1)) + 1;
757 if (!(offs & (ALIGN_SDS_BLOCK - 1)))
758 entersecurity_stuff(vol, offs);
760 * now write the security attr to storage :
761 * first data, then SII, then SDH
762 * If failure occurs while writing SDS, data will never
763 * be accessed through indexes, and will be overwritten
764 * by the next allocated descriptor
765 * If failure occurs while writing SII, the id has not
766 * recorded and will be reallocated later
767 * If failure occurs while writing SDH, the space allocated
768 * in SDS or SII will not be reused, an inconsistency
769 * will persist with no significant consequence
771 if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
772 || entersecurity_indexes(vol, attrsz, hash, securid, offs))
773 securid = const_cpu_to_le32(0);
775 /* inode now is dirty, synchronize it all */
776 ntfs_index_entry_mark_dirty(vol->secure_xsii);
777 ntfs_index_ctx_reinit(vol->secure_xsii);
778 ntfs_index_entry_mark_dirty(vol->secure_xsdh);
779 ntfs_index_ctx_reinit(vol->secure_xsdh);
780 NInoSetDirty(vol->secure_ni);
781 if (ntfs_inode_sync(vol->secure_ni))
782 ntfs_log_perror("Could not sync $Secure\n");
783 return (securid);
787 * Find a matching security descriptor in $Secure,
788 * if none, allocate a new id and write the descriptor to storage
789 * Returns id of entry, or zero if there is a problem.
791 * important : calls have to be serialized, however no locking is
792 * needed while fuse is not multithreaded
795 static le32 setsecurityattr(ntfs_volume *vol,
796 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
798 struct SDH *psdh; /* this is an image of index (le) */
799 union {
800 struct {
801 le32 dataoffsl;
802 le32 dataoffsh;
803 } parts;
804 le64 all;
805 } realign;
806 BOOL found;
807 BOOL collision;
808 size_t size;
809 size_t rdsize;
810 s64 offs;
811 int res;
812 ntfs_index_context *xsdh;
813 char *oldattr;
814 SDH_INDEX_KEY key;
815 INDEX_ENTRY *entry;
816 le32 securid;
817 le32 hash;
818 int olderrno;
820 hash = ntfs_security_hash(attr,attrsz);
821 oldattr = (char*)NULL;
822 securid = const_cpu_to_le32(0);
823 res = 0;
824 xsdh = vol->secure_xsdh;
825 if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
826 ntfs_index_ctx_reinit(xsdh);
828 * find the nearest key as (hash,0)
829 * (do not search for partial key : in case of collision,
830 * it could return a key which is not the first one which
831 * collides)
833 key.hash = hash;
834 key.security_id = const_cpu_to_le32(0);
835 olderrno = errno;
836 found = !ntfs_index_lookup((char*)&key,
837 sizeof(SDH_INDEX_KEY), xsdh);
838 if (!found && (errno != ENOENT))
839 ntfs_log_perror("Inconsistency in index $SDH");
840 else {
841 /* restore errno to avoid misinterpretation */
842 errno = olderrno;
843 entry = xsdh->entry;
844 found = FALSE;
846 * lookup() may return a node with no data,
847 * if so get next
849 if (entry->ie_flags & INDEX_ENTRY_END)
850 entry = ntfs_index_next(entry,xsdh);
851 do {
852 collision = FALSE;
853 psdh = (struct SDH*)entry;
854 if (psdh)
855 size = (size_t) le32_to_cpu(psdh->datasize)
856 - sizeof(SECURITY_DESCRIPTOR_HEADER);
857 else size = 0;
858 /* if hash is not the same, the key is not present */
859 if (psdh && (size > 0)
860 && (psdh->keyhash == hash)) {
861 /* if hash is the same */
862 /* check the whole record */
863 realign.parts.dataoffsh = psdh->dataoffsh;
864 realign.parts.dataoffsl = psdh->dataoffsl;
865 offs = le64_to_cpu(realign.all)
866 + sizeof(SECURITY_DESCRIPTOR_HEADER);
867 oldattr = (char*)ntfs_malloc(size);
868 if (oldattr) {
869 rdsize = ntfs_attr_data_read(
870 vol->secure_ni,
871 STREAM_SDS, 4,
872 oldattr, size, offs);
873 found = (rdsize == size)
874 && !memcmp(oldattr,attr,size);
875 free(oldattr);
876 /* if the records do not compare */
877 /* (hash collision), try next one */
878 if (!found) {
879 entry = ntfs_index_next(
880 entry,xsdh);
881 collision = TRUE;
883 } else
884 res = ENOMEM;
886 } while (collision && entry);
887 if (found)
888 securid = psdh->keysecurid;
889 else {
890 if (res) {
891 errno = res;
892 securid = const_cpu_to_le32(0);
893 } else {
895 * no matching key :
896 * have to build a new one
898 securid = entersecurityattr(vol,
899 attr, attrsz, hash);
904 if (--vol->secure_reentry)
905 ntfs_log_perror("Reentry error, check no multithreading\n");
906 return (securid);
911 * Update the security descriptor of a file
912 * Either as an attribute (complying with pre v3.x NTFS version)
913 * or, when possible, as an entry in $Secure (for NTFS v3.x)
915 * returns 0 if success
918 static int update_secur_descr(ntfs_volume *vol,
919 char *newattr, ntfs_inode *ni)
921 int newattrsz;
922 int written;
923 int res;
924 ntfs_attr *na;
926 newattrsz = ntfs_attr_size(newattr);
928 #if !FORCE_FORMAT_v1x
929 if ((vol->major_ver < 3) || !vol->secure_ni) {
930 #endif
932 /* update for NTFS format v1.x */
934 /* update the old security attribute */
935 na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
936 if (na) {
937 /* resize attribute */
938 res = ntfs_attr_truncate(na, (s64) newattrsz);
939 /* overwrite value */
940 if (!res) {
941 written = (int)ntfs_attr_pwrite(na, (s64) 0,
942 (s64) newattrsz, newattr);
943 if (written != newattrsz) {
944 ntfs_log_error("Failed to update "
945 "a v1.x security descriptor\n");
946 errno = EIO;
947 res = -1;
951 ntfs_attr_close(na);
952 /* if old security attribute was found, also */
953 /* truncate standard information attribute to v1.x */
954 /* this is needed when security data is wanted */
955 /* as v1.x though volume is formatted for v3.x */
956 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
957 AT_UNNAMED, 0);
958 if (na) {
959 clear_nino_flag(ni, v3_Extensions);
961 * Truncating the record does not sweep extensions
962 * from copy in memory. Clear security_id to be safe
964 ni->security_id = const_cpu_to_le32(0);
965 res = ntfs_attr_truncate(na, (s64)48);
966 ntfs_attr_close(na);
967 clear_nino_flag(ni, v3_Extensions);
969 } else {
971 * insert the new security attribute if there
972 * were none
974 res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
975 AT_UNNAMED, 0, (u8*)newattr,
976 (s64) newattrsz);
978 #if !FORCE_FORMAT_v1x
979 } else {
981 /* update for NTFS format v3.x */
983 le32 securid;
985 securid = setsecurityattr(vol,
986 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
987 (s64)newattrsz);
988 if (securid) {
989 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
990 AT_UNNAMED, 0);
991 if (na) {
992 res = 0;
993 if (!test_nino_flag(ni, v3_Extensions)) {
994 /* expand standard information attribute to v3.x */
995 res = ntfs_attr_truncate(na,
996 (s64)sizeof(STANDARD_INFORMATION));
997 ni->owner_id = const_cpu_to_le32(0);
998 ni->quota_charged = const_cpu_to_le64(0);
999 ni->usn = const_cpu_to_le64(0);
1000 ntfs_attr_remove(ni,
1001 AT_SECURITY_DESCRIPTOR,
1002 AT_UNNAMED, 0);
1004 set_nino_flag(ni, v3_Extensions);
1005 ni->security_id = securid;
1006 ntfs_attr_close(na);
1007 } else {
1008 ntfs_log_error("Failed to update "
1009 "standard informations\n");
1010 errno = EIO;
1011 res = -1;
1013 } else
1014 res = -1;
1016 #endif
1018 /* mark node as dirty */
1019 NInoSetDirty(ni);
1020 return (res);
1024 * Upgrade the security descriptor of a file
1025 * This is intended to allow graceful upgrades for files which
1026 * were created in previous versions, with a security attributes
1027 * and no security id.
1029 * It will allocate a security id and replace the individual
1030 * security attribute by a reference to the global one
1032 * Special files are not upgraded (currently / and files in
1033 * directories /$*)
1035 * Though most code is similar to update_secur_desc() it has
1036 * been kept apart to facilitate the further processing of
1037 * special cases or even to remove it if found dangerous.
1039 * returns 0 if success,
1040 * 1 if not upgradable. This is not an error.
1041 * -1 if there is a problem
1044 static int upgrade_secur_desc(ntfs_volume *vol,
1045 const char *attr, ntfs_inode *ni)
1047 int attrsz;
1048 int res;
1049 le32 securid;
1050 ntfs_attr *na;
1053 * upgrade requires NTFS format v3.x
1054 * also refuse upgrading for special files
1055 * whose number is less than FILE_first_user
1058 if ((vol->major_ver >= 3)
1059 && (ni->mft_no >= FILE_first_user)) {
1060 attrsz = ntfs_attr_size(attr);
1061 securid = setsecurityattr(vol,
1062 (const SECURITY_DESCRIPTOR_RELATIVE*)attr,
1063 (s64)attrsz);
1064 if (securid) {
1065 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1066 AT_UNNAMED, 0);
1067 if (na) {
1068 /* expand standard information attribute to v3.x */
1069 res = ntfs_attr_truncate(na,
1070 (s64)sizeof(STANDARD_INFORMATION));
1071 ni->owner_id = const_cpu_to_le32(0);
1072 ni->quota_charged = const_cpu_to_le64(0);
1073 ni->usn = const_cpu_to_le64(0);
1074 ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
1075 AT_UNNAMED, 0);
1076 set_nino_flag(ni, v3_Extensions);
1077 ni->security_id = securid;
1078 ntfs_attr_close(na);
1079 } else {
1080 ntfs_log_error("Failed to upgrade "
1081 "standard informations\n");
1082 errno = EIO;
1083 res = -1;
1085 } else
1086 res = -1;
1087 /* mark node as dirty */
1088 NInoSetDirty(ni);
1089 } else
1090 res = 1;
1092 return (res);
1096 * Optional simplified checking of group membership
1098 * This only takes into account the groups defined in
1099 * /etc/group at initialization time.
1100 * It does not take into account the groups dynamically set by
1101 * setgroups() nor the changes in /etc/group since initialization
1103 * This optional method could be useful if standard checking
1104 * leads to a performance concern.
1106 * Should not be called for user root, however the group may be root
1110 static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1112 BOOL ingroup;
1113 int grcnt;
1114 gid_t *groups;
1115 struct MAPPING *user;
1117 ingroup = FALSE;
1118 if (uid) {
1119 user = scx->mapping[MAPUSERS];
1120 while (user && ((uid_t)user->xid != uid))
1121 user = user->next;
1122 if (user) {
1123 groups = user->groups;
1124 grcnt = user->grcnt;
1125 while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
1126 ingroup = (grcnt >= 0);
1129 return (ingroup);
1132 #if defined(__sun) && defined (__SVR4)
1135 * Check whether current thread owner is member of file group
1136 * Solaris/OpenIndiana version
1137 * Should not be called for user root, however the group may be root
1139 * The group list is available in "/proc/$PID/cred"
1143 static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1145 typedef struct prcred {
1146 uid_t pr_euid; /* effective user id */
1147 uid_t pr_ruid; /* real user id */
1148 uid_t pr_suid; /* saved user id (from exec) */
1149 gid_t pr_egid; /* effective group id */
1150 gid_t pr_rgid; /* real group id */
1151 gid_t pr_sgid; /* saved group id (from exec) */
1152 int pr_ngroups; /* number of supplementary groups */
1153 gid_t pr_groups[1]; /* array of supplementary groups */
1154 } prcred_t;
1155 enum { readset = 16 };
1157 prcred_t basecreds;
1158 gid_t groups[readset];
1159 char filename[64];
1160 int fd;
1161 int k;
1162 int cnt;
1163 gid_t *p;
1164 BOOL ismember;
1165 int got;
1166 pid_t tid;
1168 if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
1169 ismember = staticgroupmember(scx, uid, gid);
1170 else {
1171 ismember = FALSE; /* default return */
1172 tid = scx->tid;
1173 sprintf(filename,"/proc/%u/cred",tid);
1174 fd = open(filename,O_RDONLY);
1175 if (fd >= 0) {
1176 got = read(fd, &basecreds, sizeof(prcred_t));
1177 if (got == sizeof(prcred_t)) {
1178 if (basecreds.pr_egid == gid)
1179 ismember = TRUE;
1180 p = basecreds.pr_groups;
1181 cnt = 1;
1182 k = 0;
1183 while (!ismember
1184 && (k < basecreds.pr_ngroups)
1185 && (cnt > 0)
1186 && (*p != gid)) {
1187 k++;
1188 cnt--;
1189 p++;
1190 if (cnt <= 0) {
1191 got = read(fd, groups,
1192 readset*sizeof(gid_t));
1193 cnt = got/sizeof(gid_t);
1194 p = groups;
1197 if ((cnt > 0)
1198 && (k < basecreds.pr_ngroups))
1199 ismember = TRUE;
1201 close(fd);
1204 return (ismember);
1207 #else /* defined(__sun) && defined (__SVR4) */
1210 * Check whether current thread owner is member of file group
1211 * Linux version
1212 * Should not be called for user root, however the group may be root
1214 * As indicated by Miklos Szeredi :
1216 * The group list is available in
1218 * /proc/$PID/task/$TID/status
1220 * and fuse supplies TID in get_fuse_context()->pid. The only problem is
1221 * finding out PID, for which I have no good solution, except to iterate
1222 * through all processes. This is rather slow, but may be speeded up
1223 * with caching and heuristics (for single threaded programs PID = TID).
1225 * The following implementation gets the group list from
1226 * /proc/$TID/task/$TID/status which apparently exists and
1227 * contains the same data.
1230 static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1232 static char key[] = "\nGroups:";
1233 char buf[BUFSZ+1];
1234 char filename[64];
1235 enum { INKEY, INSEP, INNUM, INEND } state;
1236 int fd;
1237 char c;
1238 int matched;
1239 BOOL ismember;
1240 int got;
1241 char *p;
1242 gid_t grp;
1243 pid_t tid;
1245 if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
1246 ismember = staticgroupmember(scx, uid, gid);
1247 else {
1248 ismember = FALSE; /* default return */
1249 tid = scx->tid;
1250 sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
1251 fd = open(filename,O_RDONLY);
1252 if (fd >= 0) {
1253 got = read(fd, buf, BUFSZ);
1254 buf[got] = 0;
1255 state = INKEY;
1256 matched = 0;
1257 p = buf;
1258 grp = 0;
1260 * A simple automaton to process lines like
1261 * Groups: 14 500 513
1263 do {
1264 c = *p++;
1265 if (!c) {
1266 /* refill buffer */
1267 got = read(fd, buf, BUFSZ);
1268 buf[got] = 0;
1269 p = buf;
1270 c = *p++; /* 0 at end of file */
1272 switch (state) {
1273 case INKEY :
1274 if (key[matched] == c) {
1275 if (!key[++matched])
1276 state = INSEP;
1277 } else
1278 if (key[0] == c)
1279 matched = 1;
1280 else
1281 matched = 0;
1282 break;
1283 case INSEP :
1284 if ((c >= '0') && (c <= '9')) {
1285 grp = c - '0';
1286 state = INNUM;
1287 } else
1288 if ((c != ' ') && (c != '\t'))
1289 state = INEND;
1290 break;
1291 case INNUM :
1292 if ((c >= '0') && (c <= '9'))
1293 grp = grp*10 + c - '0';
1294 else {
1295 ismember = (grp == gid);
1296 if ((c != ' ') && (c != '\t'))
1297 state = INEND;
1298 else
1299 state = INSEP;
1301 default :
1302 break;
1304 } while (!ismember && c && (state != INEND));
1305 close(fd);
1306 if (!c)
1307 ntfs_log_error("No group record found in %s\n",filename);
1308 } else
1309 ntfs_log_error("Could not open %s\n",filename);
1311 return (ismember);
1314 #endif /* defined(__sun) && defined (__SVR4) */
1317 * Cacheing is done two-way :
1318 * - from uid, gid and perm to securid (CACHED_SECURID)
1319 * - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
1321 * CACHED_SECURID data is kept in a most-recent-first list
1322 * which should not be too long to be efficient. Its optimal
1323 * size is depends on usage and is hard to determine.
1325 * CACHED_PERMISSIONS data is kept in a two-level indexed array. It
1326 * is optimal at the expense of storage. Use of a most-recent-first
1327 * list would save memory and provide similar performances for
1328 * standard usage, but not for file servers with too many file
1329 * owners
1331 * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
1332 * for legacy directories which were not allocated a security_id
1333 * it is organized in a most-recent-first list.
1335 * In main caches, data is never invalidated, as the meaning of
1336 * a security_id only changes when user mapping is changed, which
1337 * current implies remounting. However returned entries may be
1338 * overwritten at next update, so data has to be copied elsewhere
1339 * before another cache update is made.
1340 * In legacy cache, data has to be invalidated when protection is
1341 * changed.
1343 * Though the same data may be found in both list, they
1344 * must be kept separately : the interpretation of ACL
1345 * in both direction are approximations which could be non
1346 * reciprocal for some configuration of the user mapping data
1348 * During the process of recompiling ntfs-3g from a tgz archive,
1349 * security processing added 7.6% to the cpu time used by ntfs-3g
1350 * and 30% if the cache is disabled.
1353 static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
1354 u32 securindex)
1356 struct PERMISSIONS_CACHE *cache;
1357 unsigned int index1;
1358 unsigned int i;
1360 cache = (struct PERMISSIONS_CACHE*)NULL;
1361 /* create the first permissions blocks */
1362 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1363 cache = (struct PERMISSIONS_CACHE*)
1364 ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
1365 + index1*sizeof(struct CACHED_PERMISSIONS*));
1366 if (cache) {
1367 cache->head.last = index1;
1368 cache->head.p_reads = 0;
1369 cache->head.p_hits = 0;
1370 cache->head.p_writes = 0;
1371 *scx->pseccache = cache;
1372 for (i=0; i<=index1; i++)
1373 cache->cachetable[i]
1374 = (struct CACHED_PERMISSIONS*)NULL;
1376 return (cache);
1380 * Free memory used by caches
1381 * The only purpose is to facilitate the detection of memory leaks
1384 static void free_caches(struct SECURITY_CONTEXT *scx)
1386 unsigned int index1;
1387 struct PERMISSIONS_CACHE *pseccache;
1389 pseccache = *scx->pseccache;
1390 if (pseccache) {
1391 for (index1=0; index1<=pseccache->head.last; index1++)
1392 if (pseccache->cachetable[index1]) {
1393 #if POSIXACLS
1394 struct CACHED_PERMISSIONS *cacheentry;
1395 unsigned int index2;
1397 for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
1398 cacheentry = &pseccache->cachetable[index1][index2];
1399 if (cacheentry->valid
1400 && cacheentry->pxdesc)
1401 free(cacheentry->pxdesc);
1403 #endif
1404 free(pseccache->cachetable[index1]);
1406 free(pseccache);
1410 static int compare(const struct CACHED_SECURID *cached,
1411 const struct CACHED_SECURID *item)
1413 #if POSIXACLS
1414 size_t csize;
1415 size_t isize;
1417 /* only compare data and sizes */
1418 csize = (cached->variable ?
1419 sizeof(struct POSIX_ACL)
1420 + (((struct POSIX_SECURITY*)cached->variable)->acccnt
1421 + ((struct POSIX_SECURITY*)cached->variable)->defcnt)
1422 *sizeof(struct POSIX_ACE) :
1424 isize = (item->variable ?
1425 sizeof(struct POSIX_ACL)
1426 + (((struct POSIX_SECURITY*)item->variable)->acccnt
1427 + ((struct POSIX_SECURITY*)item->variable)->defcnt)
1428 *sizeof(struct POSIX_ACE) :
1430 return ((cached->uid != item->uid)
1431 || (cached->gid != item->gid)
1432 || (cached->dmode != item->dmode)
1433 || (csize != isize)
1434 || (csize
1435 && isize
1436 && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
1437 &((struct POSIX_SECURITY*)item->variable)->acl, csize)));
1438 #else
1439 return ((cached->uid != item->uid)
1440 || (cached->gid != item->gid)
1441 || (cached->dmode != item->dmode));
1442 #endif
1445 static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
1446 const struct CACHED_PERMISSIONS_LEGACY *item)
1448 return (cached->mft_no != item->mft_no);
1452 * Resize permission cache table
1453 * do not call unless resizing is needed
1455 * If allocation fails, the cache size is not updated
1456 * Lack of memory is not considered as an error, the cache is left
1457 * consistent and errno is not set.
1460 static void resize_cache(struct SECURITY_CONTEXT *scx,
1461 u32 securindex)
1463 struct PERMISSIONS_CACHE *oldcache;
1464 struct PERMISSIONS_CACHE *newcache;
1465 int newcnt;
1466 int oldcnt;
1467 unsigned int index1;
1468 unsigned int i;
1470 oldcache = *scx->pseccache;
1471 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1472 newcnt = index1 + 1;
1473 if (newcnt <= ((CACHE_PERMISSIONS_SIZE
1474 + (1 << CACHE_PERMISSIONS_BITS)
1475 - 1) >> CACHE_PERMISSIONS_BITS)) {
1476 /* expand cache beyond current end, do not use realloc() */
1477 /* to avoid losing data when there is no more memory */
1478 oldcnt = oldcache->head.last + 1;
1479 newcache = (struct PERMISSIONS_CACHE*)
1480 ntfs_malloc(
1481 sizeof(struct PERMISSIONS_CACHE)
1482 + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1483 if (newcache) {
1484 memcpy(newcache,oldcache,
1485 sizeof(struct PERMISSIONS_CACHE)
1486 + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1487 free(oldcache);
1488 /* mark new entries as not valid */
1489 for (i=newcache->head.last+1; i<=index1; i++)
1490 newcache->cachetable[i]
1491 = (struct CACHED_PERMISSIONS*)NULL;
1492 newcache->head.last = index1;
1493 *scx->pseccache = newcache;
1499 * Enter uid, gid and mode into cache, if possible
1501 * returns the updated or created cache entry,
1502 * or NULL if not possible (typically if there is no
1503 * security id associated)
1506 #if POSIXACLS
1507 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1508 ntfs_inode *ni, uid_t uid, gid_t gid,
1509 struct POSIX_SECURITY *pxdesc)
1510 #else
1511 static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1512 ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
1513 #endif
1515 struct CACHED_PERMISSIONS *cacheentry;
1516 struct CACHED_PERMISSIONS *cacheblock;
1517 struct PERMISSIONS_CACHE *pcache;
1518 u32 securindex;
1519 #if POSIXACLS
1520 int pxsize;
1521 struct POSIX_SECURITY *pxcached;
1522 #endif
1523 unsigned int index1;
1524 unsigned int index2;
1525 int i;
1527 /* cacheing is only possible if a security_id has been defined */
1528 if (test_nino_flag(ni, v3_Extensions)
1529 && ni->security_id) {
1531 * Immediately test the most frequent situation
1532 * where the entry exists
1534 securindex = le32_to_cpu(ni->security_id);
1535 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1536 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1537 pcache = *scx->pseccache;
1538 if (pcache
1539 && (pcache->head.last >= index1)
1540 && pcache->cachetable[index1]) {
1541 cacheentry = &pcache->cachetable[index1][index2];
1542 cacheentry->uid = uid;
1543 cacheentry->gid = gid;
1544 #if POSIXACLS
1545 if (cacheentry->valid && cacheentry->pxdesc)
1546 free(cacheentry->pxdesc);
1547 if (pxdesc) {
1548 pxsize = sizeof(struct POSIX_SECURITY)
1549 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1550 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1551 if (pxcached) {
1552 memcpy(pxcached, pxdesc, pxsize);
1553 cacheentry->pxdesc = pxcached;
1554 } else {
1555 cacheentry->valid = 0;
1556 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1558 cacheentry->mode = pxdesc->mode & 07777;
1559 } else
1560 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1561 #else
1562 cacheentry->mode = mode & 07777;
1563 #endif
1564 cacheentry->inh_fileid = const_cpu_to_le32(0);
1565 cacheentry->inh_dirid = const_cpu_to_le32(0);
1566 cacheentry->valid = 1;
1567 pcache->head.p_writes++;
1568 } else {
1569 if (!pcache) {
1570 /* create the first cache block */
1571 pcache = create_caches(scx, securindex);
1572 } else {
1573 if (index1 > pcache->head.last) {
1574 resize_cache(scx, securindex);
1575 pcache = *scx->pseccache;
1578 /* allocate block, if cache table was allocated */
1579 if (pcache && (index1 <= pcache->head.last)) {
1580 cacheblock = (struct CACHED_PERMISSIONS*)
1581 malloc(sizeof(struct CACHED_PERMISSIONS)
1582 << CACHE_PERMISSIONS_BITS);
1583 pcache->cachetable[index1] = cacheblock;
1584 for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
1585 cacheblock[i].valid = 0;
1586 cacheentry = &cacheblock[index2];
1587 if (cacheentry) {
1588 cacheentry->uid = uid;
1589 cacheentry->gid = gid;
1590 #if POSIXACLS
1591 if (pxdesc) {
1592 pxsize = sizeof(struct POSIX_SECURITY)
1593 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1594 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1595 if (pxcached) {
1596 memcpy(pxcached, pxdesc, pxsize);
1597 cacheentry->pxdesc = pxcached;
1598 } else {
1599 cacheentry->valid = 0;
1600 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1602 cacheentry->mode = pxdesc->mode & 07777;
1603 } else
1604 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1605 #else
1606 cacheentry->mode = mode & 07777;
1607 #endif
1608 cacheentry->inh_fileid = const_cpu_to_le32(0);
1609 cacheentry->inh_dirid = const_cpu_to_le32(0);
1610 cacheentry->valid = 1;
1611 pcache->head.p_writes++;
1613 } else
1614 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1616 } else {
1617 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1618 #if CACHE_LEGACY_SIZE
1619 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1620 struct CACHED_PERMISSIONS_LEGACY wanted;
1621 struct CACHED_PERMISSIONS_LEGACY *legacy;
1623 wanted.perm.uid = uid;
1624 wanted.perm.gid = gid;
1625 #if POSIXACLS
1626 wanted.perm.mode = pxdesc->mode & 07777;
1627 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1628 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1629 wanted.mft_no = ni->mft_no;
1630 wanted.variable = (void*)pxdesc;
1631 wanted.varsize = sizeof(struct POSIX_SECURITY)
1632 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1633 #else
1634 wanted.perm.mode = mode & 07777;
1635 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1636 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1637 wanted.mft_no = ni->mft_no;
1638 wanted.variable = (void*)NULL;
1639 wanted.varsize = 0;
1640 #endif
1641 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
1642 scx->vol->legacy_cache, GENERIC(&wanted),
1643 (cache_compare)leg_compare);
1644 if (legacy) {
1645 cacheentry = &legacy->perm;
1646 #if POSIXACLS
1648 * give direct access to the cached pxdesc
1649 * in the permissions structure
1651 cacheentry->pxdesc = legacy->variable;
1652 #endif
1655 #endif
1657 return (cacheentry);
1661 * Fetch owner, group and permission of a file, if cached
1663 * Beware : do not use the returned entry after a cache update :
1664 * the cache may be relocated making the returned entry meaningless
1666 * returns the cache entry, or NULL if not available
1669 static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
1670 ntfs_inode *ni)
1672 struct CACHED_PERMISSIONS *cacheentry;
1673 struct PERMISSIONS_CACHE *pcache;
1674 u32 securindex;
1675 unsigned int index1;
1676 unsigned int index2;
1678 /* cacheing is only possible if a security_id has been defined */
1679 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1680 if (test_nino_flag(ni, v3_Extensions)
1681 && (ni->security_id)) {
1682 securindex = le32_to_cpu(ni->security_id);
1683 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1684 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1685 pcache = *scx->pseccache;
1686 if (pcache
1687 && (pcache->head.last >= index1)
1688 && pcache->cachetable[index1]) {
1689 cacheentry = &pcache->cachetable[index1][index2];
1690 /* reject if entry is not valid */
1691 if (!cacheentry->valid)
1692 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1693 else
1694 pcache->head.p_hits++;
1695 if (pcache)
1696 pcache->head.p_reads++;
1699 #if CACHE_LEGACY_SIZE
1700 else {
1701 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1702 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1703 struct CACHED_PERMISSIONS_LEGACY wanted;
1704 struct CACHED_PERMISSIONS_LEGACY *legacy;
1706 wanted.mft_no = ni->mft_no;
1707 wanted.variable = (void*)NULL;
1708 wanted.varsize = 0;
1709 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
1710 scx->vol->legacy_cache, GENERIC(&wanted),
1711 (cache_compare)leg_compare);
1712 if (legacy) cacheentry = &legacy->perm;
1715 #endif
1716 #if POSIXACLS
1717 if (cacheentry && !cacheentry->pxdesc) {
1718 ntfs_log_error("No Posix descriptor in cache\n");
1719 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1721 #endif
1722 return (cacheentry);
1726 * Retrieve a security attribute from $Secure
1729 static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
1731 struct SII *psii;
1732 union {
1733 struct {
1734 le32 dataoffsl;
1735 le32 dataoffsh;
1736 } parts;
1737 le64 all;
1738 } realign;
1739 int found;
1740 size_t size;
1741 size_t rdsize;
1742 s64 offs;
1743 ntfs_inode *ni;
1744 ntfs_index_context *xsii;
1745 char *securattr;
1747 securattr = (char*)NULL;
1748 ni = vol->secure_ni;
1749 xsii = vol->secure_xsii;
1750 if (ni && xsii) {
1751 ntfs_index_ctx_reinit(xsii);
1752 found =
1753 !ntfs_index_lookup((char*)&id,
1754 sizeof(SII_INDEX_KEY), xsii);
1755 if (found) {
1756 psii = (struct SII*)xsii->entry;
1757 size =
1758 (size_t) le32_to_cpu(psii->datasize)
1759 - sizeof(SECURITY_DESCRIPTOR_HEADER);
1760 /* work around bad alignment problem */
1761 realign.parts.dataoffsh = psii->dataoffsh;
1762 realign.parts.dataoffsl = psii->dataoffsl;
1763 offs = le64_to_cpu(realign.all)
1764 + sizeof(SECURITY_DESCRIPTOR_HEADER);
1766 securattr = (char*)ntfs_malloc(size);
1767 if (securattr) {
1768 rdsize = ntfs_attr_data_read(
1769 ni, STREAM_SDS, 4,
1770 securattr, size, offs);
1771 if ((rdsize != size)
1772 || !ntfs_valid_descr(securattr,
1773 rdsize)) {
1774 /* error to be logged by caller */
1775 free(securattr);
1776 securattr = (char*)NULL;
1779 } else
1780 if (errno != ENOENT)
1781 ntfs_log_perror("Inconsistency in index $SII");
1783 if (!securattr) {
1784 ntfs_log_error("Failed to retrieve a security descriptor\n");
1785 errno = EIO;
1787 return (securattr);
1791 * Get the security descriptor associated to a file
1793 * Either :
1794 * - read the security descriptor attribute (v1.x format)
1795 * - or find the descriptor in $Secure:$SDS (v3.x format)
1797 * in both case, sanity checks are done on the attribute and
1798 * the descriptor can be assumed safe
1800 * The returned descriptor is dynamically allocated and has to be freed
1803 static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
1805 SII_INDEX_KEY securid;
1806 char *securattr;
1807 s64 readallsz;
1810 * Warning : in some situations, after fixing by chkdsk,
1811 * v3_Extensions are marked present (long standard informations)
1812 * with a default security descriptor inserted in an
1813 * attribute
1815 if (test_nino_flag(ni, v3_Extensions)
1816 && vol->secure_ni && ni->security_id) {
1817 /* get v3.x descriptor in $Secure */
1818 securid.security_id = ni->security_id;
1819 securattr = retrievesecurityattr(vol,securid);
1820 if (!securattr)
1821 ntfs_log_error("Bad security descriptor for 0x%lx\n",
1822 (long)le32_to_cpu(ni->security_id));
1823 } else {
1824 /* get v1.x security attribute */
1825 readallsz = 0;
1826 securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
1827 AT_UNNAMED, 0, &readallsz);
1828 if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
1829 ntfs_log_error("Bad security descriptor for inode %lld\n",
1830 (long long)ni->mft_no);
1831 free(securattr);
1832 securattr = (char*)NULL;
1835 if (!securattr) {
1837 * in some situations, there is no security
1838 * descriptor, and chkdsk does not detect or fix
1839 * anything. This could be a normal situation.
1840 * When this happens, simulate a descriptor with
1841 * minimum rights, so that a real descriptor can
1842 * be created by chown or chmod
1844 ntfs_log_error("No security descriptor found for inode %lld\n",
1845 (long long)ni->mft_no);
1846 securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
1848 return (securattr);
1851 #if POSIXACLS
1854 * Determine which access types to a file are allowed
1855 * according to the relation of current process to the file
1857 * Do not call if default_permissions is set
1860 static int access_check_posix(struct SECURITY_CONTEXT *scx,
1861 struct POSIX_SECURITY *pxdesc, mode_t request,
1862 uid_t uid, gid_t gid)
1864 struct POSIX_ACE *pxace;
1865 int userperms;
1866 int groupperms;
1867 int mask;
1868 BOOL somegroup;
1869 BOOL needgroups;
1870 mode_t perms;
1871 int i;
1873 perms = pxdesc->mode;
1874 /* owner and root access */
1875 if (!scx->uid || (uid == scx->uid)) {
1876 if (!scx->uid) {
1877 /* root access if owner or other execution */
1878 if (perms & 0101)
1879 perms |= 01777;
1880 else {
1881 /* root access if some group execution */
1882 groupperms = 0;
1883 mask = 7;
1884 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1885 pxace = &pxdesc->acl.ace[i];
1886 switch (pxace->tag) {
1887 case POSIX_ACL_USER_OBJ :
1888 case POSIX_ACL_GROUP_OBJ :
1889 case POSIX_ACL_GROUP :
1890 groupperms |= pxace->perms;
1891 break;
1892 case POSIX_ACL_MASK :
1893 mask = pxace->perms & 7;
1894 break;
1895 default :
1896 break;
1899 perms = (groupperms & mask & 1) | 6;
1901 } else
1902 perms &= 07700;
1903 } else {
1905 * analyze designated users, get mask
1906 * and identify whether we need to check
1907 * the group memberships. The groups are
1908 * not needed when all groups have the
1909 * same permissions as other for the
1910 * requested modes.
1912 userperms = -1;
1913 groupperms = -1;
1914 needgroups = FALSE;
1915 mask = 7;
1916 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1917 pxace = &pxdesc->acl.ace[i];
1918 switch (pxace->tag) {
1919 case POSIX_ACL_USER :
1920 if ((uid_t)pxace->id == scx->uid)
1921 userperms = pxace->perms;
1922 break;
1923 case POSIX_ACL_MASK :
1924 mask = pxace->perms & 7;
1925 break;
1926 case POSIX_ACL_GROUP_OBJ :
1927 case POSIX_ACL_GROUP :
1928 if (((pxace->perms & mask) ^ perms)
1929 & (request >> 6) & 7)
1930 needgroups = TRUE;
1931 break;
1932 default :
1933 break;
1936 /* designated users */
1937 if (userperms >= 0)
1938 perms = (perms & 07000) + (userperms & mask);
1939 else if (!needgroups)
1940 perms &= 07007;
1941 else {
1942 /* owning group */
1943 if (!(~(perms >> 3) & request & mask)
1944 && ((gid == scx->gid)
1945 || groupmember(scx, scx->uid, gid)))
1946 perms &= 07070;
1947 else {
1948 /* other groups */
1949 groupperms = -1;
1950 somegroup = FALSE;
1951 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1952 pxace = &pxdesc->acl.ace[i];
1953 if ((pxace->tag == POSIX_ACL_GROUP)
1954 && groupmember(scx, uid, pxace->id)) {
1955 if (!(~pxace->perms & request & mask))
1956 groupperms = pxace->perms;
1957 somegroup = TRUE;
1960 if (groupperms >= 0)
1961 perms = (perms & 07000) + (groupperms & mask);
1962 else
1963 if (somegroup)
1964 perms = 0;
1965 else
1966 perms &= 07007;
1970 return (perms);
1974 * Get permissions to access a file
1975 * Takes into account the relation of user to file (owner, group, ...)
1976 * Do no use as mode of the file
1977 * Do no call if default_permissions is set
1979 * returns -1 if there is a problem
1982 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
1983 ntfs_inode * ni, mode_t request)
1985 const SECURITY_DESCRIPTOR_RELATIVE *phead;
1986 const struct CACHED_PERMISSIONS *cached;
1987 char *securattr;
1988 const SID *usid; /* owner of file/directory */
1989 const SID *gsid; /* group of file/directory */
1990 uid_t uid;
1991 gid_t gid;
1992 int perm;
1993 BOOL isdir;
1994 struct POSIX_SECURITY *pxdesc;
1996 if (!scx->mapping[MAPUSERS])
1997 perm = 07777;
1998 else {
1999 /* check whether available in cache */
2000 cached = fetch_cache(scx,ni);
2001 if (cached) {
2002 uid = cached->uid;
2003 gid = cached->gid;
2004 perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
2005 } else {
2006 perm = 0; /* default to no permission */
2007 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2008 != const_cpu_to_le16(0);
2009 securattr = getsecurityattr(scx->vol, ni);
2010 if (securattr) {
2011 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2012 securattr;
2013 gsid = (const SID*)&
2014 securattr[le32_to_cpu(phead->group)];
2015 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2016 #if OWNERFROMACL
2017 usid = ntfs_acl_owner(securattr);
2018 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2019 usid, gsid, isdir);
2020 if (pxdesc)
2021 perm = pxdesc->mode & 07777;
2022 else
2023 perm = -1;
2024 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2025 #else
2026 usid = (const SID*)&
2027 securattr[le32_to_cpu(phead->owner)];
2028 pxdesc = ntfs_build_permissions_posix(scx,securattr,
2029 usid, gsid, isdir);
2030 if (pxdesc)
2031 perm = pxdesc->mode & 07777;
2032 else
2033 perm = -1;
2034 if (!perm && ntfs_same_sid(usid, adminsid)) {
2035 uid = find_tenant(scx, securattr);
2036 if (uid)
2037 perm = 0700;
2038 } else
2039 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2040 #endif
2042 * Create a security id if there were none
2043 * and upgrade option is selected
2045 if (!test_nino_flag(ni, v3_Extensions)
2046 && (perm >= 0)
2047 && (scx->vol->secure_flags
2048 & (1 << SECURITY_ADDSECURIDS))) {
2049 upgrade_secur_desc(scx->vol,
2050 securattr, ni);
2052 * fetch owner and group for cacheing
2053 * if there is a securid
2056 if (test_nino_flag(ni, v3_Extensions)
2057 && (perm >= 0)) {
2058 enter_cache(scx, ni, uid,
2059 gid, pxdesc);
2061 if (pxdesc) {
2062 perm = access_check_posix(scx,pxdesc,request,uid,gid);
2063 free(pxdesc);
2065 free(securattr);
2066 } else {
2067 perm = -1;
2068 uid = gid = 0;
2072 return (perm);
2076 * Get a Posix ACL
2078 * returns size or -errno if there is a problem
2079 * if size was too small, no copy is done and errno is not set,
2080 * the caller is expected to issue a new call
2083 int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2084 const char *name, char *value, size_t size)
2086 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2087 struct POSIX_SECURITY *pxdesc;
2088 const struct CACHED_PERMISSIONS *cached;
2089 char *securattr;
2090 const SID *usid; /* owner of file/directory */
2091 const SID *gsid; /* group of file/directory */
2092 uid_t uid;
2093 gid_t gid;
2094 BOOL isdir;
2095 size_t outsize;
2097 outsize = 0; /* default to error */
2098 if (!scx->mapping[MAPUSERS])
2099 errno = ENOTSUP;
2100 else {
2101 /* check whether available in cache */
2102 cached = fetch_cache(scx,ni);
2103 if (cached)
2104 pxdesc = cached->pxdesc;
2105 else {
2106 securattr = getsecurityattr(scx->vol, ni);
2107 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2108 != const_cpu_to_le16(0);
2109 if (securattr) {
2110 phead =
2111 (const SECURITY_DESCRIPTOR_RELATIVE*)
2112 securattr;
2113 gsid = (const SID*)&
2114 securattr[le32_to_cpu(phead->group)];
2115 #if OWNERFROMACL
2116 usid = ntfs_acl_owner(securattr);
2117 #else
2118 usid = (const SID*)&
2119 securattr[le32_to_cpu(phead->owner)];
2120 #endif
2121 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2122 usid, gsid, isdir);
2125 * fetch owner and group for cacheing
2127 if (pxdesc) {
2129 * Create a security id if there were none
2130 * and upgrade option is selected
2132 if (!test_nino_flag(ni, v3_Extensions)
2133 && (scx->vol->secure_flags
2134 & (1 << SECURITY_ADDSECURIDS))) {
2135 upgrade_secur_desc(scx->vol,
2136 securattr, ni);
2138 #if OWNERFROMACL
2139 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2140 #else
2141 if (!(pxdesc->mode & 07777)
2142 && ntfs_same_sid(usid, adminsid)) {
2143 uid = find_tenant(scx,
2144 securattr);
2145 } else
2146 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2147 #endif
2148 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2149 if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
2150 enter_cache(scx, ni, uid,
2151 gid, pxdesc);
2153 free(securattr);
2154 } else
2155 pxdesc = (struct POSIX_SECURITY*)NULL;
2158 if (pxdesc) {
2159 if (ntfs_valid_posix(pxdesc)) {
2160 if (!strcmp(name,"system.posix_acl_default")) {
2161 if (ni->mrec->flags
2162 & MFT_RECORD_IS_DIRECTORY)
2163 outsize = sizeof(struct POSIX_ACL)
2164 + pxdesc->defcnt*sizeof(struct POSIX_ACE);
2165 else {
2167 * getting default ACL from plain file :
2168 * return EACCES if size > 0 as
2169 * indicated in the man, but return ok
2170 * if size == 0, so that ls does not
2171 * display an error
2173 if (size > 0) {
2174 outsize = 0;
2175 errno = EACCES;
2176 } else
2177 outsize = sizeof(struct POSIX_ACL);
2179 if (outsize && (outsize <= size)) {
2180 memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
2181 memcpy(&value[sizeof(struct POSIX_ACL)],
2182 &pxdesc->acl.ace[pxdesc->firstdef],
2183 outsize-sizeof(struct POSIX_ACL));
2185 } else {
2186 outsize = sizeof(struct POSIX_ACL)
2187 + pxdesc->acccnt*sizeof(struct POSIX_ACE);
2188 if (outsize <= size)
2189 memcpy(value,&pxdesc->acl,outsize);
2191 } else {
2192 outsize = 0;
2193 errno = EIO;
2194 ntfs_log_error("Invalid Posix ACL built\n");
2196 if (!cached)
2197 free(pxdesc);
2198 } else
2199 outsize = 0;
2201 return (outsize ? (int)outsize : -errno);
2204 #else /* POSIXACLS */
2208 * Get permissions to access a file
2209 * Takes into account the relation of user to file (owner, group, ...)
2210 * Do no use as mode of the file
2212 * returns -1 if there is a problem
2215 static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
2216 ntfs_inode *ni, mode_t request)
2218 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2219 const struct CACHED_PERMISSIONS *cached;
2220 char *securattr;
2221 const SID *usid; /* owner of file/directory */
2222 const SID *gsid; /* group of file/directory */
2223 BOOL isdir;
2224 uid_t uid;
2225 gid_t gid;
2226 int perm;
2228 if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
2229 perm = 07777;
2230 else {
2231 /* check whether available in cache */
2232 cached = fetch_cache(scx,ni);
2233 if (cached) {
2234 perm = cached->mode;
2235 uid = cached->uid;
2236 gid = cached->gid;
2237 } else {
2238 perm = 0; /* default to no permission */
2239 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2240 != const_cpu_to_le16(0);
2241 securattr = getsecurityattr(scx->vol, ni);
2242 if (securattr) {
2243 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2244 securattr;
2245 gsid = (const SID*)&
2246 securattr[le32_to_cpu(phead->group)];
2247 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2248 #if OWNERFROMACL
2249 usid = ntfs_acl_owner(securattr);
2250 perm = ntfs_build_permissions(securattr,
2251 usid, gsid, isdir);
2252 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2253 #else
2254 usid = (const SID*)&
2255 securattr[le32_to_cpu(phead->owner)];
2256 perm = ntfs_build_permissions(securattr,
2257 usid, gsid, isdir);
2258 if (!perm && ntfs_same_sid(usid, adminsid)) {
2259 uid = find_tenant(scx, securattr);
2260 if (uid)
2261 perm = 0700;
2262 } else
2263 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2264 #endif
2266 * Create a security id if there were none
2267 * and upgrade option is selected
2269 if (!test_nino_flag(ni, v3_Extensions)
2270 && (perm >= 0)
2271 && (scx->vol->secure_flags
2272 & (1 << SECURITY_ADDSECURIDS))) {
2273 upgrade_secur_desc(scx->vol,
2274 securattr, ni);
2276 * fetch owner and group for cacheing
2277 * if there is a securid
2280 if (test_nino_flag(ni, v3_Extensions)
2281 && (perm >= 0)) {
2282 enter_cache(scx, ni, uid,
2283 gid, perm);
2285 free(securattr);
2286 } else {
2287 perm = -1;
2288 uid = gid = 0;
2291 if (perm >= 0) {
2292 if (!scx->uid) {
2293 /* root access and execution */
2294 if (perm & 0111)
2295 perm |= 01777;
2296 else
2297 perm = 0;
2298 } else
2299 if (uid == scx->uid)
2300 perm &= 07700;
2301 else
2303 * avoid checking group membership
2304 * when the requested perms for group
2305 * are the same as perms for other
2307 if ((gid == scx->gid)
2308 || ((((perm >> 3) ^ perm)
2309 & (request >> 6) & 7)
2310 && groupmember(scx, scx->uid, gid)))
2311 perm &= 07070;
2312 else
2313 perm &= 07007;
2316 return (perm);
2319 #endif /* POSIXACLS */
2322 * Get an NTFS ACL
2324 * Returns size or -errno if there is a problem
2325 * if size was too small, no copy is done and errno is not set,
2326 * the caller is expected to issue a new call
2329 int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2330 char *value, size_t size)
2332 char *securattr;
2333 size_t outsize;
2335 outsize = 0; /* default to no data and no error */
2336 securattr = getsecurityattr(scx->vol, ni);
2337 if (securattr) {
2338 outsize = ntfs_attr_size(securattr);
2339 if (outsize <= size) {
2340 memcpy(value,securattr,outsize);
2342 free(securattr);
2344 return (outsize ? (int)outsize : -errno);
2348 * Get owner, group and permissions in an stat structure
2349 * returns permissions, or -1 if there is a problem
2352 int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
2353 ntfs_inode * ni, struct stat *stbuf)
2355 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2356 char *securattr;
2357 const SID *usid; /* owner of file/directory */
2358 const SID *gsid; /* group of file/directory */
2359 const struct CACHED_PERMISSIONS *cached;
2360 int perm;
2361 BOOL isdir;
2362 #if POSIXACLS
2363 struct POSIX_SECURITY *pxdesc;
2364 #endif
2366 if (!scx->mapping[MAPUSERS])
2367 perm = 07777;
2368 else {
2369 /* check whether available in cache */
2370 cached = fetch_cache(scx,ni);
2371 if (cached) {
2372 perm = cached->mode;
2373 stbuf->st_uid = cached->uid;
2374 stbuf->st_gid = cached->gid;
2375 stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
2376 } else {
2377 perm = -1; /* default to error */
2378 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2379 != const_cpu_to_le16(0);
2380 securattr = getsecurityattr(scx->vol, ni);
2381 if (securattr) {
2382 phead =
2383 (const SECURITY_DESCRIPTOR_RELATIVE*)
2384 securattr;
2385 gsid = (const SID*)&
2386 securattr[le32_to_cpu(phead->group)];
2387 #if OWNERFROMACL
2388 usid = ntfs_acl_owner(securattr);
2389 #else
2390 usid = (const SID*)&
2391 securattr[le32_to_cpu(phead->owner)];
2392 #endif
2393 #if POSIXACLS
2394 pxdesc = ntfs_build_permissions_posix(scx->mapping, securattr,
2395 usid, gsid, isdir);
2396 if (pxdesc)
2397 perm = pxdesc->mode & 07777;
2398 else
2399 perm = -1;
2400 #else
2401 perm = ntfs_build_permissions(securattr,
2402 usid, gsid, isdir);
2403 #endif
2405 * fetch owner and group for cacheing
2407 if (perm >= 0) {
2409 * Create a security id if there were none
2410 * and upgrade option is selected
2412 if (!test_nino_flag(ni, v3_Extensions)
2413 && (scx->vol->secure_flags
2414 & (1 << SECURITY_ADDSECURIDS))) {
2415 upgrade_secur_desc(scx->vol,
2416 securattr, ni);
2418 #if OWNERFROMACL
2419 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2420 #else
2421 if (!perm && ntfs_same_sid(usid, adminsid)) {
2422 stbuf->st_uid =
2423 find_tenant(scx,
2424 securattr);
2425 if (stbuf->st_uid)
2426 perm = 0700;
2427 } else
2428 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2429 #endif
2430 stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2431 stbuf->st_mode =
2432 (stbuf->st_mode & ~07777) + perm;
2433 #if POSIXACLS
2434 enter_cache(scx, ni, stbuf->st_uid,
2435 stbuf->st_gid, pxdesc);
2436 free(pxdesc);
2437 #else
2438 enter_cache(scx, ni, stbuf->st_uid,
2439 stbuf->st_gid, perm);
2440 #endif
2442 free(securattr);
2446 return (perm);
2449 #if POSIXACLS
2452 * Get the base for a Posix inheritance and
2453 * build an inherited Posix descriptor
2456 static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
2457 ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
2459 const struct CACHED_PERMISSIONS *cached;
2460 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2461 struct POSIX_SECURITY *pxdesc;
2462 struct POSIX_SECURITY *pydesc;
2463 char *securattr;
2464 const SID *usid;
2465 const SID *gsid;
2466 uid_t uid;
2467 gid_t gid;
2469 pydesc = (struct POSIX_SECURITY*)NULL;
2470 /* check whether parent directory is available in cache */
2471 cached = fetch_cache(scx,dir_ni);
2472 if (cached) {
2473 uid = cached->uid;
2474 gid = cached->gid;
2475 pxdesc = cached->pxdesc;
2476 if (pxdesc) {
2477 pydesc = ntfs_build_inherited_posix(pxdesc,mode,
2478 scx->umask,isdir);
2480 } else {
2481 securattr = getsecurityattr(scx->vol, dir_ni);
2482 if (securattr) {
2483 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2484 securattr;
2485 gsid = (const SID*)&
2486 securattr[le32_to_cpu(phead->group)];
2487 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2488 #if OWNERFROMACL
2489 usid = ntfs_acl_owner(securattr);
2490 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2491 usid, gsid, TRUE);
2492 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2493 #else
2494 usid = (const SID*)&
2495 securattr[le32_to_cpu(phead->owner)];
2496 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2497 usid, gsid, TRUE);
2498 if (pxdesc && ntfs_same_sid(usid, adminsid)) {
2499 uid = find_tenant(scx, securattr);
2500 } else
2501 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2502 #endif
2503 if (pxdesc) {
2505 * Create a security id if there were none
2506 * and upgrade option is selected
2508 if (!test_nino_flag(dir_ni, v3_Extensions)
2509 && (scx->vol->secure_flags
2510 & (1 << SECURITY_ADDSECURIDS))) {
2511 upgrade_secur_desc(scx->vol,
2512 securattr, dir_ni);
2514 * fetch owner and group for cacheing
2515 * if there is a securid
2518 if (test_nino_flag(dir_ni, v3_Extensions)) {
2519 enter_cache(scx, dir_ni, uid,
2520 gid, pxdesc);
2522 pydesc = ntfs_build_inherited_posix(pxdesc,
2523 mode, scx->umask, isdir);
2524 free(pxdesc);
2526 free(securattr);
2529 return (pydesc);
2533 * Allocate a security_id for a file being created
2535 * Returns zero if not possible (NTFS v3.x required)
2538 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2539 uid_t uid, gid_t gid, ntfs_inode *dir_ni,
2540 mode_t mode, BOOL isdir)
2542 #if !FORCE_FORMAT_v1x
2543 const struct CACHED_SECURID *cached;
2544 struct CACHED_SECURID wanted;
2545 struct POSIX_SECURITY *pxdesc;
2546 char *newattr;
2547 int newattrsz;
2548 const SID *usid;
2549 const SID *gsid;
2550 BIGSID defusid;
2551 BIGSID defgsid;
2552 le32 securid;
2553 #endif
2555 securid = const_cpu_to_le32(0);
2557 #if !FORCE_FORMAT_v1x
2559 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2560 if (pxdesc) {
2561 /* check whether target securid is known in cache */
2563 wanted.uid = uid;
2564 wanted.gid = gid;
2565 wanted.dmode = pxdesc->mode & mode & 07777;
2566 if (isdir) wanted.dmode |= 0x10000;
2567 wanted.variable = (void*)pxdesc;
2568 wanted.varsize = sizeof(struct POSIX_SECURITY)
2569 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2570 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2571 scx->vol->securid_cache, GENERIC(&wanted),
2572 (cache_compare)compare);
2573 /* quite simple, if we are lucky */
2574 if (cached)
2575 securid = cached->securid;
2577 /* not in cache : make sure we can create ids */
2579 if (!cached && (scx->vol->major_ver >= 3)) {
2580 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2581 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2582 if (!usid || !gsid) {
2583 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2584 (int)uid, (int)gid);
2585 usid = gsid = adminsid;
2587 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2588 isdir, usid, gsid);
2589 if (newattr) {
2590 newattrsz = ntfs_attr_size(newattr);
2591 securid = setsecurityattr(scx->vol,
2592 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2593 newattrsz);
2594 if (securid) {
2595 /* update cache, for subsequent use */
2596 wanted.securid = securid;
2597 ntfs_enter_cache(scx->vol->securid_cache,
2598 GENERIC(&wanted),
2599 (cache_compare)compare);
2601 free(newattr);
2602 } else {
2604 * could not build new security attribute
2605 * errno set by ntfs_build_descr()
2609 free(pxdesc);
2611 #endif
2612 return (securid);
2616 * Apply Posix inheritance to a newly created file
2617 * (for NTFS 1.x only : no securid)
2620 int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
2621 ntfs_inode *ni, uid_t uid, gid_t gid,
2622 ntfs_inode *dir_ni, mode_t mode)
2624 struct POSIX_SECURITY *pxdesc;
2625 char *newattr;
2626 const SID *usid;
2627 const SID *gsid;
2628 BIGSID defusid;
2629 BIGSID defgsid;
2630 BOOL isdir;
2631 int res;
2633 res = -1;
2634 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2635 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2636 if (pxdesc) {
2637 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2638 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2639 if (!usid || !gsid) {
2640 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2641 (int)uid, (int)gid);
2642 usid = gsid = adminsid;
2644 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2645 isdir, usid, gsid);
2646 if (newattr) {
2647 /* Adjust Windows read-only flag */
2648 res = update_secur_descr(scx->vol, newattr, ni);
2649 if (!res && !isdir) {
2650 if (mode & S_IWUSR)
2651 ni->flags &= ~FILE_ATTR_READONLY;
2652 else
2653 ni->flags |= FILE_ATTR_READONLY;
2655 #if CACHE_LEGACY_SIZE
2656 /* also invalidate legacy cache */
2657 if (isdir && !ni->security_id) {
2658 struct CACHED_PERMISSIONS_LEGACY legacy;
2660 legacy.mft_no = ni->mft_no;
2661 legacy.variable = pxdesc;
2662 legacy.varsize = sizeof(struct POSIX_SECURITY)
2663 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2664 ntfs_invalidate_cache(scx->vol->legacy_cache,
2665 GENERIC(&legacy),
2666 (cache_compare)leg_compare,0);
2668 #endif
2669 free(newattr);
2671 } else {
2673 * could not build new security attribute
2674 * errno set by ntfs_build_descr()
2678 return (res);
2681 #else
2683 le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2684 uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
2686 #if !FORCE_FORMAT_v1x
2687 const struct CACHED_SECURID *cached;
2688 struct CACHED_SECURID wanted;
2689 char *newattr;
2690 int newattrsz;
2691 const SID *usid;
2692 const SID *gsid;
2693 BIGSID defusid;
2694 BIGSID defgsid;
2695 le32 securid;
2696 #endif
2698 securid = const_cpu_to_le32(0);
2700 #if !FORCE_FORMAT_v1x
2701 /* check whether target securid is known in cache */
2703 wanted.uid = uid;
2704 wanted.gid = gid;
2705 wanted.dmode = mode & 07777;
2706 if (isdir) wanted.dmode |= 0x10000;
2707 wanted.variable = (void*)NULL;
2708 wanted.varsize = 0;
2709 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2710 scx->vol->securid_cache, GENERIC(&wanted),
2711 (cache_compare)compare);
2712 /* quite simple, if we are lucky */
2713 if (cached)
2714 securid = cached->securid;
2716 /* not in cache : make sure we can create ids */
2718 if (!cached && (scx->vol->major_ver >= 3)) {
2719 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2720 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2721 if (!usid || !gsid) {
2722 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2723 (int)uid, (int)gid);
2724 usid = gsid = adminsid;
2726 newattr = ntfs_build_descr(mode, isdir, usid, gsid);
2727 if (newattr) {
2728 newattrsz = ntfs_attr_size(newattr);
2729 securid = setsecurityattr(scx->vol,
2730 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2731 newattrsz);
2732 if (securid) {
2733 /* update cache, for subsequent use */
2734 wanted.securid = securid;
2735 ntfs_enter_cache(scx->vol->securid_cache,
2736 GENERIC(&wanted),
2737 (cache_compare)compare);
2739 free(newattr);
2740 } else {
2742 * could not build new security attribute
2743 * errno set by ntfs_build_descr()
2747 #endif
2748 return (securid);
2751 #endif
2754 * Update ownership and mode of a file, reusing an existing
2755 * security descriptor when possible
2757 * Returns zero if successful
2760 #if POSIXACLS
2761 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2762 uid_t uid, gid_t gid, mode_t mode,
2763 struct POSIX_SECURITY *pxdesc)
2764 #else
2765 int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2766 uid_t uid, gid_t gid, mode_t mode)
2767 #endif
2769 int res;
2770 const struct CACHED_SECURID *cached;
2771 struct CACHED_SECURID wanted;
2772 char *newattr;
2773 const SID *usid;
2774 const SID *gsid;
2775 BIGSID defusid;
2776 BIGSID defgsid;
2777 BOOL isdir;
2779 res = 0;
2781 /* check whether target securid is known in cache */
2783 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2784 wanted.uid = uid;
2785 wanted.gid = gid;
2786 wanted.dmode = mode & 07777;
2787 if (isdir) wanted.dmode |= 0x10000;
2788 #if POSIXACLS
2789 wanted.variable = (void*)pxdesc;
2790 if (pxdesc)
2791 wanted.varsize = sizeof(struct POSIX_SECURITY)
2792 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2793 else
2794 wanted.varsize = 0;
2795 #else
2796 wanted.variable = (void*)NULL;
2797 wanted.varsize = 0;
2798 #endif
2799 if (test_nino_flag(ni, v3_Extensions)) {
2800 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2801 scx->vol->securid_cache, GENERIC(&wanted),
2802 (cache_compare)compare);
2803 /* quite simple, if we are lucky */
2804 if (cached) {
2805 ni->security_id = cached->securid;
2806 NInoSetDirty(ni);
2808 } else cached = (struct CACHED_SECURID*)NULL;
2810 if (!cached) {
2812 * Do not use usid and gsid from former attributes,
2813 * but recompute them to get repeatable results
2814 * which can be kept in cache.
2816 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2817 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2818 if (!usid || !gsid) {
2819 ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
2820 uid, gid);
2821 usid = gsid = adminsid;
2823 #if POSIXACLS
2824 if (pxdesc)
2825 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2826 isdir, usid, gsid);
2827 else
2828 newattr = ntfs_build_descr(mode,
2829 isdir, usid, gsid);
2830 #else
2831 newattr = ntfs_build_descr(mode,
2832 isdir, usid, gsid);
2833 #endif
2834 if (newattr) {
2835 res = update_secur_descr(scx->vol, newattr, ni);
2836 if (!res) {
2837 /* adjust Windows read-only flag */
2838 if (!isdir) {
2839 if (mode & S_IWUSR)
2840 ni->flags &= ~FILE_ATTR_READONLY;
2841 else
2842 ni->flags |= FILE_ATTR_READONLY;
2843 NInoFileNameSetDirty(ni);
2845 /* update cache, for subsequent use */
2846 if (test_nino_flag(ni, v3_Extensions)) {
2847 wanted.securid = ni->security_id;
2848 ntfs_enter_cache(scx->vol->securid_cache,
2849 GENERIC(&wanted),
2850 (cache_compare)compare);
2852 #if CACHE_LEGACY_SIZE
2853 /* also invalidate legacy cache */
2854 if (isdir && !ni->security_id) {
2855 struct CACHED_PERMISSIONS_LEGACY legacy;
2857 legacy.mft_no = ni->mft_no;
2858 #if POSIXACLS
2859 legacy.variable = wanted.variable;
2860 legacy.varsize = wanted.varsize;
2861 #else
2862 legacy.variable = (void*)NULL;
2863 legacy.varsize = 0;
2864 #endif
2865 ntfs_invalidate_cache(scx->vol->legacy_cache,
2866 GENERIC(&legacy),
2867 (cache_compare)leg_compare,0);
2869 #endif
2871 free(newattr);
2872 } else {
2874 * could not build new security attribute
2875 * errno set by ntfs_build_descr()
2877 res = -1;
2880 return (res);
2884 * Check whether user has ownership rights on a file
2886 * Returns TRUE if allowed
2887 * if not, errno tells why
2890 BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
2892 const struct CACHED_PERMISSIONS *cached;
2893 char *oldattr;
2894 const SID *usid;
2895 uid_t processuid;
2896 uid_t uid;
2897 BOOL gotowner;
2898 int allowed;
2900 processuid = scx->uid;
2901 /* TODO : use CAP_FOWNER process capability */
2903 * Always allow for root
2904 * Also always allow if no mapping has been defined
2906 if (!scx->mapping[MAPUSERS] || !processuid)
2907 allowed = TRUE;
2908 else {
2909 gotowner = FALSE; /* default */
2910 /* get the owner, either from cache or from old attribute */
2911 cached = fetch_cache(scx, ni);
2912 if (cached) {
2913 uid = cached->uid;
2914 gotowner = TRUE;
2915 } else {
2916 oldattr = getsecurityattr(scx->vol, ni);
2917 if (oldattr) {
2918 #if OWNERFROMACL
2919 usid = ntfs_acl_owner(oldattr);
2920 #else
2921 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2923 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2924 oldattr;
2925 usid = (const SID*)&oldattr
2926 [le32_to_cpu(phead->owner)];
2927 #endif
2928 uid = ntfs_find_user(scx->mapping[MAPUSERS],
2929 usid);
2930 gotowner = TRUE;
2931 free(oldattr);
2934 allowed = FALSE;
2935 if (gotowner) {
2936 /* TODO : use CAP_FOWNER process capability */
2937 if (!processuid || (processuid == uid))
2938 allowed = TRUE;
2939 else
2940 errno = EPERM;
2943 return (allowed);
2946 #ifdef HAVE_SETXATTR /* extended attributes interface required */
2948 #if POSIXACLS
2951 * Set a new access or default Posix ACL to a file
2952 * (or remove ACL if no input data)
2953 * Validity of input data is checked after merging
2955 * Returns 0, or -1 if there is a problem which errno describes
2958 int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2959 const char *name, const char *value, size_t size,
2960 int flags)
2962 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2963 const struct CACHED_PERMISSIONS *cached;
2964 char *oldattr;
2965 uid_t processuid;
2966 const SID *usid;
2967 const SID *gsid;
2968 uid_t uid;
2969 uid_t gid;
2970 int res;
2971 BOOL isdir;
2972 BOOL deflt;
2973 BOOL exist;
2974 int count;
2975 struct POSIX_SECURITY *oldpxdesc;
2976 struct POSIX_SECURITY *newpxdesc;
2978 /* get the current pxsec, either from cache or from old attribute */
2979 res = -1;
2980 deflt = !strcmp(name,"system.posix_acl_default");
2981 if (size)
2982 count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
2983 else
2984 count = 0;
2985 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2986 newpxdesc = (struct POSIX_SECURITY*)NULL;
2987 if ((!value
2988 || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION))
2989 && (!deflt || isdir || (!size && !value))) {
2990 cached = fetch_cache(scx, ni);
2991 if (cached) {
2992 uid = cached->uid;
2993 gid = cached->gid;
2994 oldpxdesc = cached->pxdesc;
2995 if (oldpxdesc) {
2996 newpxdesc = ntfs_replace_acl(oldpxdesc,
2997 (const struct POSIX_ACL*)value,count,deflt);
2999 } else {
3000 oldattr = getsecurityattr(scx->vol, ni);
3001 if (oldattr) {
3002 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
3003 #if OWNERFROMACL
3004 usid = ntfs_acl_owner(oldattr);
3005 #else
3006 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
3007 #endif
3008 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
3009 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3010 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3011 oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
3012 oldattr, usid, gsid, isdir);
3013 if (oldpxdesc) {
3014 if (deflt)
3015 exist = oldpxdesc->defcnt > 0;
3016 else
3017 exist = oldpxdesc->acccnt > 3;
3018 if ((exist && (flags & XATTR_CREATE))
3019 || (!exist && (flags & XATTR_REPLACE))) {
3020 errno = (exist ? EEXIST : ENODATA);
3021 } else {
3022 newpxdesc = ntfs_replace_acl(oldpxdesc,
3023 (const struct POSIX_ACL*)value,count,deflt);
3025 free(oldpxdesc);
3027 free(oldattr);
3030 } else
3031 errno = EINVAL;
3033 if (newpxdesc) {
3034 processuid = scx->uid;
3035 /* TODO : use CAP_FOWNER process capability */
3036 if (!processuid || (uid == processuid)) {
3038 * clear setgid if file group does
3039 * not match process group
3041 if (processuid && (gid != scx->gid)
3042 && !groupmember(scx, scx->uid, gid)) {
3043 newpxdesc->mode &= ~S_ISGID;
3045 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3046 newpxdesc->mode, newpxdesc);
3047 } else
3048 errno = EPERM;
3049 free(newpxdesc);
3051 return (res ? -1 : 0);
3055 * Remove a default Posix ACL from a file
3057 * Returns 0, or -1 if there is a problem which errno describes
3060 int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3061 const char *name)
3063 return (ntfs_set_posix_acl(scx, ni, name,
3064 (const char*)NULL, 0, 0));
3067 #endif
3070 * Set a new NTFS ACL to a file
3072 * Returns 0, or -1 if there is a problem
3075 int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3076 const char *value, size_t size, int flags)
3078 char *attr;
3079 int res;
3081 res = -1;
3082 if ((size > 0)
3083 && !(flags & XATTR_CREATE)
3084 && ntfs_valid_descr(value,size)
3085 && (ntfs_attr_size(value) == size)) {
3086 /* need copying in order to write */
3087 attr = (char*)ntfs_malloc(size);
3088 if (attr) {
3089 memcpy(attr,value,size);
3090 res = update_secur_descr(scx->vol, attr, ni);
3092 * No need to invalidate standard caches :
3093 * the relation between a securid and
3094 * the associated protection is unchanged,
3095 * only the relation between a file and
3096 * its securid and protection is changed.
3098 #if CACHE_LEGACY_SIZE
3100 * we must however invalidate the legacy
3101 * cache, which is based on inode numbers.
3102 * For safety, invalidate even if updating
3103 * failed.
3105 if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3106 && !ni->security_id) {
3107 struct CACHED_PERMISSIONS_LEGACY legacy;
3109 legacy.mft_no = ni->mft_no;
3110 legacy.variable = (char*)NULL;
3111 legacy.varsize = 0;
3112 ntfs_invalidate_cache(scx->vol->legacy_cache,
3113 GENERIC(&legacy),
3114 (cache_compare)leg_compare,0);
3116 #endif
3117 free(attr);
3118 } else
3119 errno = ENOMEM;
3120 } else
3121 errno = EINVAL;
3122 return (res ? -1 : 0);
3125 #endif /* HAVE_SETXATTR */
3128 * Set new permissions to a file
3129 * Checks user mapping has been defined before request for setting
3131 * rejected if request is not originated by owner or root
3133 * returns 0 on success
3134 * -1 on failure, with errno = EIO
3137 int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
3139 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3140 const struct CACHED_PERMISSIONS *cached;
3141 char *oldattr;
3142 const SID *usid;
3143 const SID *gsid;
3144 uid_t processuid;
3145 uid_t uid;
3146 uid_t gid;
3147 int res;
3148 #if POSIXACLS
3149 BOOL isdir;
3150 int pxsize;
3151 const struct POSIX_SECURITY *oldpxdesc;
3152 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3153 #endif
3155 /* get the current owner, either from cache or from old attribute */
3156 res = 0;
3157 cached = fetch_cache(scx, ni);
3158 if (cached) {
3159 uid = cached->uid;
3160 gid = cached->gid;
3161 #if POSIXACLS
3162 oldpxdesc = cached->pxdesc;
3163 if (oldpxdesc) {
3164 /* must copy before merging */
3165 pxsize = sizeof(struct POSIX_SECURITY)
3166 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3167 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3168 if (newpxdesc) {
3169 memcpy(newpxdesc, oldpxdesc, pxsize);
3170 if (ntfs_merge_mode_posix(newpxdesc, mode))
3171 res = -1;
3172 } else
3173 res = -1;
3174 } else
3175 newpxdesc = (struct POSIX_SECURITY*)NULL;
3176 #endif
3177 } else {
3178 oldattr = getsecurityattr(scx->vol, ni);
3179 if (oldattr) {
3180 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
3181 #if OWNERFROMACL
3182 usid = ntfs_acl_owner(oldattr);
3183 #else
3184 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
3185 #endif
3186 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
3187 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3188 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3189 #if POSIXACLS
3190 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
3191 newpxdesc = ntfs_build_permissions_posix(scx->mapping,
3192 oldattr, usid, gsid, isdir);
3193 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3194 res = -1;
3195 #endif
3196 free(oldattr);
3197 } else
3198 res = -1;
3201 if (!res) {
3202 processuid = scx->uid;
3203 /* TODO : use CAP_FOWNER process capability */
3204 if (!processuid || (uid == processuid)) {
3206 * clear setgid if file group does
3207 * not match process group
3209 if (processuid && (gid != scx->gid)
3210 && !groupmember(scx, scx->uid, gid))
3211 mode &= ~S_ISGID;
3212 #if POSIXACLS
3213 if (newpxdesc) {
3214 newpxdesc->mode = mode;
3215 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3216 mode, newpxdesc);
3217 } else
3218 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3219 mode, newpxdesc);
3220 #else
3221 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3222 #endif
3223 } else {
3224 errno = EPERM;
3225 res = -1; /* neither owner nor root */
3227 } else {
3229 * Should not happen : a default descriptor is generated
3230 * by getsecurityattr() when there are none
3232 ntfs_log_error("File has no security descriptor\n");
3233 res = -1;
3234 errno = EIO;
3236 #if POSIXACLS
3237 if (newpxdesc) free(newpxdesc);
3238 #endif
3239 return (res ? -1 : 0);
3243 * Create a default security descriptor for files whose descriptor
3244 * cannot be inherited
3247 int ntfs_sd_add_everyone(ntfs_inode *ni)
3249 /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */
3250 SECURITY_DESCRIPTOR_RELATIVE *sd;
3251 ACL *acl;
3252 ACCESS_ALLOWED_ACE *ace;
3253 SID *sid;
3254 int ret, sd_len;
3256 /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
3258 * Calculate security descriptor length. We have 2 sub-authorities in
3259 * owner and group SIDs, but structure SID contain only one, so add
3260 * 4 bytes to every SID.
3262 sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
3263 sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
3264 sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
3265 if (!sd)
3266 return -1;
3268 sd->revision = SECURITY_DESCRIPTOR_REVISION;
3269 sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
3271 sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
3272 sid->revision = SID_REVISION;
3273 sid->sub_authority_count = 2;
3274 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3275 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3276 sid->identifier_authority.value[5] = 5;
3277 sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
3279 sid = (SID*)((u8*)sid + sizeof(SID) + 4);
3280 sid->revision = SID_REVISION;
3281 sid->sub_authority_count = 2;
3282 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3283 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3284 sid->identifier_authority.value[5] = 5;
3285 sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
3287 acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
3288 acl->revision = ACL_REVISION;
3289 acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
3290 acl->ace_count = const_cpu_to_le16(1);
3291 sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
3293 ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
3294 ace->type = ACCESS_ALLOWED_ACE_TYPE;
3295 ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
3296 ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
3297 ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */
3298 ace->sid.revision = SID_REVISION;
3299 ace->sid.sub_authority_count = 1;
3300 ace->sid.sub_authority[0] = const_cpu_to_le32(0);
3301 ace->sid.identifier_authority.value[5] = 1;
3303 ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
3304 sd_len);
3305 if (ret)
3306 ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
3308 free(sd);
3309 return ret;
3313 * Check whether user can access a file in a specific way
3315 * Returns 1 if access is allowed, including user is root or no
3316 * user mapping defined
3317 * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX
3318 * 0 and sets errno if there is a problem or if access
3319 * is not allowed
3321 * This is used for Posix ACL and checking creation of DOS file names
3324 int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
3325 ntfs_inode *ni,
3326 int accesstype) /* access type required (S_Ixxx values) */
3328 int perm;
3329 int res;
3330 int allow;
3331 struct stat stbuf;
3334 * Always allow for root unless execution is requested.
3335 * (was checked by fuse until kernel 2.6.29)
3336 * Also always allow if no mapping has been defined
3338 if (!scx->mapping[MAPUSERS]
3339 || (!scx->uid
3340 && (!(accesstype & S_IEXEC)
3341 || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
3342 allow = 1;
3343 else {
3344 perm = ntfs_get_perm(scx, ni, accesstype);
3345 if (perm >= 0) {
3346 res = EACCES;
3347 switch (accesstype) {
3348 case S_IEXEC:
3349 allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
3350 break;
3351 case S_IWRITE:
3352 allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
3353 break;
3354 case S_IWRITE + S_IEXEC:
3355 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3356 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3357 break;
3358 case S_IREAD:
3359 allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
3360 break;
3361 case S_IREAD + S_IEXEC:
3362 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3363 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3364 break;
3365 case S_IREAD + S_IWRITE:
3366 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3367 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
3368 break;
3369 case S_IWRITE + S_IEXEC + S_ISVTX:
3370 if (perm & S_ISVTX) {
3371 if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3372 && (stbuf.st_uid == scx->uid))
3373 allow = 1;
3374 else
3375 allow = 2;
3376 } else
3377 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3378 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3379 break;
3380 case S_IREAD + S_IWRITE + S_IEXEC:
3381 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3382 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3383 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3384 break;
3385 default :
3386 res = EINVAL;
3387 allow = 0;
3388 break;
3390 if (!allow)
3391 errno = res;
3392 } else
3393 allow = 0;
3395 return (allow);
3399 * Check whether user can create a file (or directory)
3401 * Returns TRUE if access is allowed,
3402 * Also returns the gid and dsetgid applicable to the created file
3405 int ntfs_allowed_create(struct SECURITY_CONTEXT *scx,
3406 ntfs_inode *dir_ni, gid_t *pgid, mode_t *pdsetgid)
3408 int perm;
3409 int res;
3410 int allow;
3411 struct stat stbuf;
3414 * Always allow for root.
3415 * Also always allow if no mapping has been defined
3417 if (!scx->mapping[MAPUSERS])
3418 perm = 0777;
3419 else
3420 perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
3421 if (!scx->mapping[MAPUSERS]
3422 || !scx->uid) {
3423 allow = 1;
3424 } else {
3425 perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
3426 if (perm >= 0) {
3427 res = EACCES;
3428 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3429 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3430 if (!allow)
3431 errno = res;
3432 } else
3433 allow = 0;
3435 *pgid = scx->gid;
3436 *pdsetgid = 0;
3437 /* return directory group if S_ISGID is set */
3438 if (allow && (perm & S_ISGID)) {
3439 if (ntfs_get_owner_mode(scx, dir_ni, &stbuf) >= 0) {
3440 *pdsetgid = stbuf.st_mode & S_ISGID;
3441 if (perm & S_ISGID)
3442 *pgid = stbuf.st_gid;
3445 return (allow);
3448 #if 0 /* not needed any more */
3451 * Check whether user can access the parent directory
3452 * of a file in a specific way
3454 * Returns true if access is allowed, including user is root and
3455 * no user mapping defined
3457 * Sets errno if there is a problem or if not allowed
3459 * This is used for Posix ACL and checking creation of DOS file names
3462 BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
3463 const char *path, int accesstype)
3465 int allow;
3466 char *dirpath;
3467 char *name;
3468 ntfs_inode *ni;
3469 ntfs_inode *dir_ni;
3470 struct stat stbuf;
3472 allow = 0;
3473 dirpath = strdup(path);
3474 if (dirpath) {
3475 /* the root of file system is seen as a parent of itself */
3476 /* is that correct ? */
3477 name = strrchr(dirpath, '/');
3478 *name = 0;
3479 dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
3480 if (dir_ni) {
3481 allow = ntfs_allowed_access(scx,
3482 dir_ni, accesstype);
3483 ntfs_inode_close(dir_ni);
3485 * for an not-owned sticky directory, have to
3486 * check whether file itself is owned
3488 if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
3489 && (allow == 2)) {
3490 ni = ntfs_pathname_to_inode(scx->vol, NULL,
3491 path);
3492 allow = FALSE;
3493 if (ni) {
3494 allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3495 && (stbuf.st_uid == scx->uid);
3496 ntfs_inode_close(ni);
3500 free(dirpath);
3502 return (allow); /* errno is set if not allowed */
3505 #endif
3508 * Define a new owner/group to a file
3510 * returns zero if successful
3513 int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3514 uid_t uid, gid_t gid)
3516 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3517 const struct CACHED_PERMISSIONS *cached;
3518 char *oldattr;
3519 const SID *usid;
3520 const SID *gsid;
3521 uid_t fileuid;
3522 uid_t filegid;
3523 mode_t mode;
3524 int perm;
3525 BOOL isdir;
3526 int res;
3527 #if POSIXACLS
3528 struct POSIX_SECURITY *pxdesc;
3529 BOOL pxdescbuilt = FALSE;
3530 #endif
3532 res = 0;
3533 /* get the current owner and mode from cache or security attributes */
3534 oldattr = (char*)NULL;
3535 cached = fetch_cache(scx,ni);
3536 if (cached) {
3537 fileuid = cached->uid;
3538 filegid = cached->gid;
3539 mode = cached->mode;
3540 #if POSIXACLS
3541 pxdesc = cached->pxdesc;
3542 if (!pxdesc)
3543 res = -1;
3544 #endif
3545 } else {
3546 fileuid = 0;
3547 filegid = 0;
3548 mode = 0;
3549 oldattr = getsecurityattr(scx->vol, ni);
3550 if (oldattr) {
3551 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3552 != const_cpu_to_le16(0);
3553 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3554 oldattr;
3555 gsid = (const SID*)
3556 &oldattr[le32_to_cpu(phead->group)];
3557 #if OWNERFROMACL
3558 usid = ntfs_acl_owner(oldattr);
3559 #else
3560 usid = (const SID*)
3561 &oldattr[le32_to_cpu(phead->owner)];
3562 #endif
3563 #if POSIXACLS
3564 pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3565 usid, gsid, isdir);
3566 if (pxdesc) {
3567 pxdescbuilt = TRUE;
3568 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3569 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3570 mode = perm = pxdesc->mode;
3571 } else
3572 res = -1;
3573 #else
3574 mode = perm = ntfs_build_permissions(oldattr,
3575 usid, gsid, isdir);
3576 if (perm >= 0) {
3577 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3578 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3579 } else
3580 res = -1;
3581 #endif
3582 free(oldattr);
3583 } else
3584 res = -1;
3586 if (!res) {
3587 /* check requested by root */
3588 /* or chgrp requested by owner to an owned group */
3589 if (!scx->uid
3590 || ((((int)uid < 0) || (uid == fileuid))
3591 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3592 && (fileuid == scx->uid))) {
3593 /* replace by the new usid and gsid */
3594 /* or reuse old gid and sid for cacheing */
3595 if ((int)uid < 0)
3596 uid = fileuid;
3597 if ((int)gid < 0)
3598 gid = filegid;
3599 #if !defined(__sun) || !defined (__SVR4)
3600 /* clear setuid and setgid if owner has changed */
3601 /* unless request originated by root */
3602 if (uid && (fileuid != uid))
3603 mode &= 01777;
3604 #endif
3605 #if POSIXACLS
3606 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3607 mode, pxdesc);
3608 #else
3609 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3610 #endif
3611 } else {
3612 res = -1; /* neither owner nor root */
3613 errno = EPERM;
3615 #if POSIXACLS
3616 if (pxdescbuilt)
3617 free(pxdesc);
3618 #endif
3619 } else {
3621 * Should not happen : a default descriptor is generated
3622 * by getsecurityattr() when there are none
3624 ntfs_log_error("File has no security descriptor\n");
3625 res = -1;
3626 errno = EIO;
3628 return (res ? -1 : 0);
3632 * Define new owner/group and mode to a file
3634 * returns zero if successful
3637 int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3638 uid_t uid, gid_t gid, const mode_t mode)
3640 const struct CACHED_PERMISSIONS *cached;
3641 char *oldattr;
3642 uid_t fileuid;
3643 uid_t filegid;
3644 int res;
3645 #if POSIXACLS
3646 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3647 const SID *usid;
3648 const SID *gsid;
3649 BOOL isdir;
3650 const struct POSIX_SECURITY *oldpxdesc;
3651 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3652 int pxsize;
3653 #endif
3655 res = 0;
3656 /* get the current owner and mode from cache or security attributes */
3657 oldattr = (char*)NULL;
3658 cached = fetch_cache(scx,ni);
3659 if (cached) {
3660 fileuid = cached->uid;
3661 filegid = cached->gid;
3662 #if POSIXACLS
3663 oldpxdesc = cached->pxdesc;
3664 if (oldpxdesc) {
3665 /* must copy before merging */
3666 pxsize = sizeof(struct POSIX_SECURITY)
3667 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3668 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3669 if (newpxdesc) {
3670 memcpy(newpxdesc, oldpxdesc, pxsize);
3671 if (ntfs_merge_mode_posix(newpxdesc, mode))
3672 res = -1;
3673 } else
3674 res = -1;
3676 #endif
3677 } else {
3678 fileuid = 0;
3679 filegid = 0;
3680 oldattr = getsecurityattr(scx->vol, ni);
3681 if (oldattr) {
3682 #if POSIXACLS
3683 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3684 != const_cpu_to_le16(0);
3685 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3686 oldattr;
3687 gsid = (const SID*)
3688 &oldattr[le32_to_cpu(phead->group)];
3689 #if OWNERFROMACL
3690 usid = ntfs_acl_owner(oldattr);
3691 #else
3692 usid = (const SID*)
3693 &oldattr[le32_to_cpu(phead->owner)];
3694 #endif
3695 newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3696 usid, gsid, isdir);
3697 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3698 res = -1;
3699 else {
3700 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3701 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3703 #endif
3704 free(oldattr);
3705 } else
3706 res = -1;
3708 if (!res) {
3709 /* check requested by root */
3710 /* or chgrp requested by owner to an owned group */
3711 if (!scx->uid
3712 || ((((int)uid < 0) || (uid == fileuid))
3713 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3714 && (fileuid == scx->uid))) {
3715 /* replace by the new usid and gsid */
3716 /* or reuse old gid and sid for cacheing */
3717 if ((int)uid < 0)
3718 uid = fileuid;
3719 if ((int)gid < 0)
3720 gid = filegid;
3721 #if POSIXACLS
3722 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3723 mode, newpxdesc);
3724 #else
3725 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3726 #endif
3727 } else {
3728 res = -1; /* neither owner nor root */
3729 errno = EPERM;
3731 } else {
3733 * Should not happen : a default descriptor is generated
3734 * by getsecurityattr() when there are none
3736 ntfs_log_error("File has no security descriptor\n");
3737 res = -1;
3738 errno = EIO;
3740 #if POSIXACLS
3741 free(newpxdesc);
3742 #endif
3743 return (res ? -1 : 0);
3747 * Build a security id for a descriptor inherited from
3748 * parent directory the Windows way
3751 static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
3752 const char *parentattr, BOOL fordir)
3754 const SECURITY_DESCRIPTOR_RELATIVE *pphead;
3755 const ACL *ppacl;
3756 const SID *usid;
3757 const SID *gsid;
3758 BIGSID defusid;
3759 BIGSID defgsid;
3760 int offpacl;
3761 int offowner;
3762 int offgroup;
3763 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
3764 ACL *pnacl;
3765 int parentattrsz;
3766 char *newattr;
3767 int newattrsz;
3768 int aclsz;
3769 int usidsz;
3770 int gsidsz;
3771 int pos;
3772 le32 securid;
3774 parentattrsz = ntfs_attr_size(parentattr);
3775 pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
3776 if (scx->mapping[MAPUSERS]) {
3777 usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
3778 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
3779 if (!usid)
3780 usid = adminsid;
3781 if (!gsid)
3782 gsid = adminsid;
3783 } else {
3785 * If there is no user mapping, we have to copy owner
3786 * and group from parent directory.
3787 * Windows never has to do that, because it can always
3788 * rely on a user mapping
3790 offowner = le32_to_cpu(pphead->owner);
3791 usid = (const SID*)&parentattr[offowner];
3792 offgroup = le32_to_cpu(pphead->group);
3793 gsid = (const SID*)&parentattr[offgroup];
3796 * new attribute is smaller than parent's
3797 * except for differences in SIDs which appear in
3798 * owner, group and possible grants and denials in
3799 * generic creator-owner and creator-group ACEs.
3800 * For directories, an ACE may be duplicated for
3801 * access and inheritance, so we double the count.
3803 usidsz = ntfs_sid_size(usid);
3804 gsidsz = ntfs_sid_size(gsid);
3805 newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
3806 if (fordir)
3807 newattrsz *= 2;
3808 newattr = (char*)ntfs_malloc(newattrsz);
3809 if (newattr) {
3810 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
3811 pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
3812 pnhead->alignment = 0;
3813 pnhead->control = (pphead->control
3814 & (SE_DACL_AUTO_INHERITED | SE_SACL_AUTO_INHERITED))
3815 | SE_SELF_RELATIVE;
3816 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
3818 * locate and inherit DACL
3819 * do not test SE_DACL_PRESENT (wrong for "DR Watson")
3821 pnhead->dacl = const_cpu_to_le32(0);
3822 if (pphead->dacl) {
3823 offpacl = le32_to_cpu(pphead->dacl);
3824 ppacl = (const ACL*)&parentattr[offpacl];
3825 pnacl = (ACL*)&newattr[pos];
3826 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
3827 fordir, pphead->control
3828 & SE_DACL_AUTO_INHERITED);
3829 if (aclsz) {
3830 pnhead->dacl = cpu_to_le32(pos);
3831 pos += aclsz;
3832 pnhead->control |= SE_DACL_PRESENT;
3836 * locate and inherit SACL
3838 pnhead->sacl = const_cpu_to_le32(0);
3839 if (pphead->sacl) {
3840 offpacl = le32_to_cpu(pphead->sacl);
3841 ppacl = (const ACL*)&parentattr[offpacl];
3842 pnacl = (ACL*)&newattr[pos];
3843 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
3844 fordir, pphead->control
3845 & SE_SACL_AUTO_INHERITED);
3846 if (aclsz) {
3847 pnhead->sacl = cpu_to_le32(pos);
3848 pos += aclsz;
3849 pnhead->control |= SE_SACL_PRESENT;
3853 * inherit or redefine owner
3855 memcpy(&newattr[pos],usid,usidsz);
3856 pnhead->owner = cpu_to_le32(pos);
3857 pos += usidsz;
3859 * inherit or redefine group
3861 memcpy(&newattr[pos],gsid,gsidsz);
3862 pnhead->group = cpu_to_le32(pos);
3863 pos += gsidsz;
3864 securid = setsecurityattr(scx->vol,
3865 (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
3866 free(newattr);
3867 } else
3868 securid = const_cpu_to_le32(0);
3869 return (securid);
3873 * Get an inherited security id
3875 * For Windows compatibility, the normal initial permission setting
3876 * may be inherited from the parent directory instead of being
3877 * defined by the creation arguments.
3879 * The following creates an inherited id for that purpose.
3881 * Note : the owner and group of parent directory are also
3882 * inherited (which is not the case on Windows) if no user mapping
3883 * is defined.
3885 * Returns the inherited id, or zero if not possible (eg on NTFS 1.x)
3888 le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
3889 ntfs_inode *dir_ni, BOOL fordir)
3891 struct CACHED_PERMISSIONS *cached;
3892 char *parentattr;
3893 le32 securid;
3895 securid = const_cpu_to_le32(0);
3896 cached = (struct CACHED_PERMISSIONS*)NULL;
3898 * Try to get inherited id from cache
3900 if (test_nino_flag(dir_ni, v3_Extensions)
3901 && dir_ni->security_id) {
3902 cached = fetch_cache(scx, dir_ni);
3903 if (cached)
3904 securid = (fordir ? cached->inh_dirid
3905 : cached->inh_fileid);
3908 * Not cached or not available in cache, compute it all
3909 * Note : if parent directory has no id, it is not cacheable
3911 if (!securid) {
3912 parentattr = getsecurityattr(scx->vol, dir_ni);
3913 if (parentattr) {
3914 securid = build_inherited_id(scx,
3915 parentattr, fordir);
3916 free(parentattr);
3918 * Store the result into cache for further use
3920 if (securid) {
3921 cached = fetch_cache(scx, dir_ni);
3922 if (cached) {
3923 if (fordir)
3924 cached->inh_dirid = securid;
3925 else
3926 cached->inh_fileid = securid;
3931 return (securid);
3935 * Link a group to a member of group
3937 * Returns 0 if OK, -1 (and errno set) if error
3940 static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
3941 gid_t gid)
3943 struct group *group;
3944 char **grmem;
3945 int grcnt;
3946 gid_t *groups;
3947 int res;
3949 res = 0;
3950 group = getgrgid(gid);
3951 if (group && group->gr_mem) {
3952 grcnt = usermapping->grcnt;
3953 groups = usermapping->groups;
3954 grmem = group->gr_mem;
3955 while (*grmem && strcmp(user->pw_name, *grmem))
3956 grmem++;
3957 if (*grmem) {
3958 if (!grcnt)
3959 groups = (gid_t*)malloc(sizeof(gid_t));
3960 else
3961 groups = (gid_t*)realloc(groups,
3962 (grcnt+1)*sizeof(gid_t));
3963 if (groups)
3964 groups[grcnt++] = gid;
3965 else {
3966 res = -1;
3967 errno = ENOMEM;
3970 usermapping->grcnt = grcnt;
3971 usermapping->groups = groups;
3973 return (res);
3978 * Statically link group to users
3979 * This is based on groups defined in /etc/group and does not take
3980 * the groups dynamically set by setgroups() nor any changes in
3981 * /etc/group into account
3983 * Only mapped groups and root group are linked to mapped users
3985 * Returns 0 if OK, -1 (and errno set) if error
3989 static int link_group_members(struct SECURITY_CONTEXT *scx)
3991 struct MAPPING *usermapping;
3992 struct MAPPING *groupmapping;
3993 struct passwd *user;
3994 int res;
3996 res = 0;
3997 for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
3998 usermapping=usermapping->next) {
3999 usermapping->grcnt = 0;
4000 usermapping->groups = (gid_t*)NULL;
4001 user = getpwuid(usermapping->xid);
4002 if (user && user->pw_name) {
4003 for (groupmapping=scx->mapping[MAPGROUPS];
4004 groupmapping && !res;
4005 groupmapping=groupmapping->next) {
4006 if (link_single_group(usermapping, user,
4007 groupmapping->xid))
4008 res = -1;
4010 if (!res && link_single_group(usermapping,
4011 user, (gid_t)0))
4012 res = -1;
4015 return (res);
4019 * Apply default single user mapping
4020 * returns zero if successful
4023 static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
4024 uid_t uid, gid_t gid, const SID *usid)
4026 struct MAPPING *usermapping;
4027 struct MAPPING *groupmapping;
4028 SID *sid;
4029 int sidsz;
4030 int res;
4032 res = -1;
4033 sidsz = ntfs_sid_size(usid);
4034 sid = (SID*)ntfs_malloc(sidsz);
4035 if (sid) {
4036 memcpy(sid,usid,sidsz);
4037 usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
4038 if (usermapping) {
4039 groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
4040 if (groupmapping) {
4041 usermapping->sid = sid;
4042 usermapping->xid = uid;
4043 usermapping->next = (struct MAPPING*)NULL;
4044 groupmapping->sid = sid;
4045 groupmapping->xid = gid;
4046 groupmapping->next = (struct MAPPING*)NULL;
4047 scx->mapping[MAPUSERS] = usermapping;
4048 scx->mapping[MAPGROUPS] = groupmapping;
4049 res = 0;
4053 return (res);
4057 * Make sure there are no ambiguous mapping
4058 * Ambiguous mapping may lead to undesired configurations and
4059 * we had rather be safe until the consequences are understood
4062 #if 0 /* not activated for now */
4064 static BOOL check_mapping(const struct MAPPING *usermapping,
4065 const struct MAPPING *groupmapping)
4067 const struct MAPPING *mapping1;
4068 const struct MAPPING *mapping2;
4069 BOOL ambiguous;
4071 ambiguous = FALSE;
4072 for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
4073 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
4074 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
4075 if (mapping1->xid != mapping2->xid)
4076 ambiguous = TRUE;
4077 } else {
4078 if (mapping1->xid == mapping2->xid)
4079 ambiguous = TRUE;
4081 for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
4082 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
4083 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
4084 if (mapping1->xid != mapping2->xid)
4085 ambiguous = TRUE;
4086 } else {
4087 if (mapping1->xid == mapping2->xid)
4088 ambiguous = TRUE;
4090 return (ambiguous);
4093 #endif
4095 #if 0 /* not used any more */
4098 * Try and apply default single user mapping
4099 * returns zero if successful
4102 static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
4104 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4105 ntfs_inode *ni;
4106 char *securattr;
4107 const SID *usid;
4108 int res;
4110 res = -1;
4111 ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
4112 if (ni) {
4113 securattr = getsecurityattr(scx->vol, ni);
4114 if (securattr) {
4115 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
4116 usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
4117 if (ntfs_is_user_sid(usid))
4118 res = ntfs_do_default_mapping(scx,
4119 scx->uid, scx->gid, usid);
4120 free(securattr);
4122 ntfs_inode_close(ni);
4124 return (res);
4127 #endif
4130 * Basic read from a user mapping file on another volume
4133 static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
4135 return (read(*(int*)fileid, buf, size));
4140 * Read from a user mapping file on current NTFS partition
4143 static int localread(void *fileid, char *buf, size_t size, off_t offs)
4145 return (ntfs_attr_data_read((ntfs_inode*)fileid,
4146 AT_UNNAMED, 0, buf, size, offs));
4150 * Build the user mapping
4151 * - according to a mapping file if defined (or default present),
4152 * - or try default single user mapping if possible
4154 * The mapping is specific to a mounted device
4155 * No locking done, mounting assumed non multithreaded
4157 * returns zero if mapping is successful
4158 * (failure should not be interpreted as an error)
4161 int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
4162 BOOL allowdef)
4164 struct MAPLIST *item;
4165 struct MAPLIST *firstitem;
4166 struct MAPPING *usermapping;
4167 struct MAPPING *groupmapping;
4168 ntfs_inode *ni;
4169 int fd;
4170 static struct {
4171 u8 revision;
4172 u8 levels;
4173 be16 highbase;
4174 be32 lowbase;
4175 le32 level1;
4176 le32 level2;
4177 le32 level3;
4178 le32 level4;
4179 le32 level5;
4180 } defmap = {
4181 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
4182 const_cpu_to_le32(21),
4183 const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
4184 const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
4187 /* be sure not to map anything until done */
4188 scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
4189 scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
4191 if (!usermap_path) usermap_path = MAPPINGFILE;
4192 if (usermap_path[0] == '/') {
4193 fd = open(usermap_path,O_RDONLY);
4194 if (fd > 0) {
4195 firstitem = ntfs_read_mapping(basicread, (void*)&fd);
4196 close(fd);
4197 } else
4198 firstitem = (struct MAPLIST*)NULL;
4199 } else {
4200 ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
4201 if (ni) {
4202 firstitem = ntfs_read_mapping(localread, ni);
4203 ntfs_inode_close(ni);
4204 } else
4205 firstitem = (struct MAPLIST*)NULL;
4209 if (firstitem) {
4210 usermapping = ntfs_do_user_mapping(firstitem);
4211 groupmapping = ntfs_do_group_mapping(firstitem);
4212 if (usermapping && groupmapping) {
4213 scx->mapping[MAPUSERS] = usermapping;
4214 scx->mapping[MAPGROUPS] = groupmapping;
4215 } else
4216 ntfs_log_error("There were no valid user or no valid group\n");
4217 /* now we can free the memory copy of input text */
4218 /* and rely on internal representation */
4219 while (firstitem) {
4220 item = firstitem->next;
4221 free(firstitem);
4222 firstitem = item;
4224 } else {
4225 /* no mapping file, try a default mapping */
4226 if (allowdef) {
4227 if (!ntfs_do_default_mapping(scx,
4228 0, 0, (const SID*)&defmap))
4229 ntfs_log_info("Using default user mapping\n");
4232 return (!scx->mapping[MAPUSERS] || link_group_members(scx));
4235 #ifdef HAVE_SETXATTR /* extended attributes interface required */
4238 * Get the ntfs attribute into an extended attribute
4239 * The attribute is returned according to cpu endianness
4242 int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
4244 u32 attrib;
4245 size_t outsize;
4247 outsize = 0; /* default to no data and no error */
4248 if (ni) {
4249 attrib = le32_to_cpu(ni->flags);
4250 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4251 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4252 else
4253 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4254 if (!attrib)
4255 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4256 outsize = sizeof(FILE_ATTR_FLAGS);
4257 if (size >= outsize) {
4258 if (value)
4259 memcpy(value,&attrib,outsize);
4260 else
4261 errno = EINVAL;
4264 return (outsize ? (int)outsize : -errno);
4268 * Return the ntfs attribute into an extended attribute
4269 * The attribute is expected according to cpu endianness
4271 * Returns 0, or -1 if there is a problem
4274 int ntfs_set_ntfs_attrib(ntfs_inode *ni,
4275 const char *value, size_t size, int flags)
4277 u32 attrib;
4278 le32 settable;
4279 ATTR_FLAGS dirflags;
4280 int res;
4282 res = -1;
4283 if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
4284 if (!(flags & XATTR_CREATE)) {
4285 /* copy to avoid alignment problems */
4286 memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
4287 settable = FILE_ATTR_SETTABLE;
4288 res = 0;
4289 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4291 * Accept changing compression for a directory
4292 * and set index root accordingly
4294 settable |= FILE_ATTR_COMPRESSED;
4295 if ((ni->flags ^ cpu_to_le32(attrib))
4296 & FILE_ATTR_COMPRESSED) {
4297 if (ni->flags & FILE_ATTR_COMPRESSED)
4298 dirflags = const_cpu_to_le16(0);
4299 else
4300 dirflags = ATTR_IS_COMPRESSED;
4301 res = ntfs_attr_set_flags(ni,
4302 AT_INDEX_ROOT,
4303 NTFS_INDEX_I30, 4,
4304 dirflags,
4305 ATTR_COMPRESSION_MASK);
4308 if (!res) {
4309 ni->flags = (ni->flags & ~settable)
4310 | (cpu_to_le32(attrib) & settable);
4311 NInoFileNameSetDirty(ni);
4312 NInoSetDirty(ni);
4314 } else
4315 errno = EEXIST;
4316 } else
4317 errno = EINVAL;
4318 return (res ? -1 : 0);
4321 #endif /* HAVE_SETXATTR */
4324 * Open $Secure once for all
4325 * returns zero if it succeeds
4326 * non-zero if it fails. This is not an error (on NTFS v1.x)
4330 int ntfs_open_secure(ntfs_volume *vol)
4332 ntfs_inode *ni;
4333 int res;
4335 res = -1;
4336 vol->secure_ni = (ntfs_inode*)NULL;
4337 vol->secure_xsii = (ntfs_index_context*)NULL;
4338 vol->secure_xsdh = (ntfs_index_context*)NULL;
4339 if (vol->major_ver >= 3) {
4340 /* make sure this is a genuine $Secure inode 9 */
4341 ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
4342 if (ni && (ni->mft_no == 9)) {
4343 vol->secure_reentry = 0;
4344 vol->secure_xsii = ntfs_index_ctx_get(ni,
4345 sii_stream, 4);
4346 vol->secure_xsdh = ntfs_index_ctx_get(ni,
4347 sdh_stream, 4);
4348 if (ni && vol->secure_xsii && vol->secure_xsdh) {
4349 vol->secure_ni = ni;
4350 res = 0;
4354 return (res);
4358 * Final cleaning
4359 * Allocated memory is freed to facilitate the detection of memory leaks
4362 void ntfs_close_secure(struct SECURITY_CONTEXT *scx)
4364 ntfs_volume *vol;
4366 vol = scx->vol;
4367 if (vol->secure_ni) {
4368 ntfs_index_ctx_put(vol->secure_xsii);
4369 ntfs_index_ctx_put(vol->secure_xsdh);
4370 ntfs_inode_close(vol->secure_ni);
4373 ntfs_free_mapping(scx->mapping);
4374 free_caches(scx);
4378 * API for direct access to security descriptors
4379 * based on Win32 API
4384 * Selective feeding of a security descriptor into user buffer
4386 * Returns TRUE if successful
4389 static BOOL feedsecurityattr(const char *attr, u32 selection,
4390 char *buf, u32 buflen, u32 *psize)
4392 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4393 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
4394 const ACL *pdacl;
4395 const ACL *psacl;
4396 const SID *pusid;
4397 const SID *pgsid;
4398 unsigned int offdacl;
4399 unsigned int offsacl;
4400 unsigned int offowner;
4401 unsigned int offgroup;
4402 unsigned int daclsz;
4403 unsigned int saclsz;
4404 unsigned int usidsz;
4405 unsigned int gsidsz;
4406 unsigned int size; /* size of requested attributes */
4407 BOOL ok;
4408 unsigned int pos;
4409 unsigned int avail;
4410 le16 control;
4412 avail = 0;
4413 control = SE_SELF_RELATIVE;
4414 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4415 size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4417 /* locate DACL if requested and available */
4418 if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
4419 offdacl = le32_to_cpu(phead->dacl);
4420 pdacl = (const ACL*)&attr[offdacl];
4421 daclsz = le16_to_cpu(pdacl->size);
4422 size += daclsz;
4423 avail |= DACL_SECURITY_INFORMATION;
4424 } else
4425 offdacl = daclsz = 0;
4427 /* locate owner if requested and available */
4428 offowner = le32_to_cpu(phead->owner);
4429 if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
4430 /* find end of USID */
4431 pusid = (const SID*)&attr[offowner];
4432 usidsz = ntfs_sid_size(pusid);
4433 size += usidsz;
4434 avail |= OWNER_SECURITY_INFORMATION;
4435 } else
4436 offowner = usidsz = 0;
4438 /* locate group if requested and available */
4439 offgroup = le32_to_cpu(phead->group);
4440 if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
4441 /* find end of GSID */
4442 pgsid = (const SID*)&attr[offgroup];
4443 gsidsz = ntfs_sid_size(pgsid);
4444 size += gsidsz;
4445 avail |= GROUP_SECURITY_INFORMATION;
4446 } else
4447 offgroup = gsidsz = 0;
4449 /* locate SACL if requested and available */
4450 if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
4451 /* find end of SACL */
4452 offsacl = le32_to_cpu(phead->sacl);
4453 psacl = (const ACL*)&attr[offsacl];
4454 saclsz = le16_to_cpu(psacl->size);
4455 size += saclsz;
4456 avail |= SACL_SECURITY_INFORMATION;
4457 } else
4458 offsacl = saclsz = 0;
4461 * Check having enough size in destination buffer
4462 * (required size is returned nevertheless so that
4463 * the request can be reissued with adequate size)
4465 if (size > buflen) {
4466 *psize = size;
4467 errno = EINVAL;
4468 ok = FALSE;
4469 } else {
4470 if (selection & OWNER_SECURITY_INFORMATION)
4471 control |= phead->control & SE_OWNER_DEFAULTED;
4472 if (selection & GROUP_SECURITY_INFORMATION)
4473 control |= phead->control & SE_GROUP_DEFAULTED;
4474 if (selection & DACL_SECURITY_INFORMATION)
4475 control |= phead->control
4476 & (SE_DACL_PRESENT
4477 | SE_DACL_DEFAULTED
4478 | SE_DACL_AUTO_INHERITED
4479 | SE_DACL_PROTECTED);
4480 if (selection & SACL_SECURITY_INFORMATION)
4481 control |= phead->control
4482 & (SE_SACL_PRESENT
4483 | SE_SACL_DEFAULTED
4484 | SE_SACL_AUTO_INHERITED
4485 | SE_SACL_PROTECTED);
4487 * copy header and feed new flags, even if no detailed data
4489 memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
4490 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
4491 pnhead->control = control;
4492 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4494 /* copy DACL if requested and available */
4495 if (selection & avail & DACL_SECURITY_INFORMATION) {
4496 pnhead->dacl = cpu_to_le32(pos);
4497 memcpy(&buf[pos],&attr[offdacl],daclsz);
4498 pos += daclsz;
4499 } else
4500 pnhead->dacl = const_cpu_to_le32(0);
4502 /* copy SACL if requested and available */
4503 if (selection & avail & SACL_SECURITY_INFORMATION) {
4504 pnhead->sacl = cpu_to_le32(pos);
4505 memcpy(&buf[pos],&attr[offsacl],saclsz);
4506 pos += saclsz;
4507 } else
4508 pnhead->sacl = const_cpu_to_le32(0);
4510 /* copy owner if requested and available */
4511 if (selection & avail & OWNER_SECURITY_INFORMATION) {
4512 pnhead->owner = cpu_to_le32(pos);
4513 memcpy(&buf[pos],&attr[offowner],usidsz);
4514 pos += usidsz;
4515 } else
4516 pnhead->owner = const_cpu_to_le32(0);
4518 /* copy group if requested and available */
4519 if (selection & avail & GROUP_SECURITY_INFORMATION) {
4520 pnhead->group = cpu_to_le32(pos);
4521 memcpy(&buf[pos],&attr[offgroup],gsidsz);
4522 pos += gsidsz;
4523 } else
4524 pnhead->group = const_cpu_to_le32(0);
4525 if (pos != size)
4526 ntfs_log_error("Error in security descriptor size\n");
4527 *psize = size;
4528 ok = TRUE;
4531 return (ok);
4535 * Merge a new security descriptor into the old one
4536 * and assign to designated file
4538 * Returns TRUE if successful
4541 static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
4542 const char *newattr, u32 selection, ntfs_inode *ni)
4544 const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
4545 const SECURITY_DESCRIPTOR_RELATIVE *newhead;
4546 SECURITY_DESCRIPTOR_RELATIVE *targhead;
4547 const ACL *pdacl;
4548 const ACL *psacl;
4549 const SID *powner;
4550 const SID *pgroup;
4551 int offdacl;
4552 int offsacl;
4553 int offowner;
4554 int offgroup;
4555 unsigned int size;
4556 le16 control;
4557 char *target;
4558 int pos;
4559 int oldattrsz;
4560 int newattrsz;
4561 BOOL ok;
4563 ok = FALSE; /* default return */
4564 oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
4565 newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
4566 oldattrsz = ntfs_attr_size(oldattr);
4567 newattrsz = ntfs_attr_size(newattr);
4568 target = (char*)ntfs_malloc(oldattrsz + newattrsz);
4569 if (target) {
4570 targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
4571 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4572 control = SE_SELF_RELATIVE;
4574 * copy new DACL if selected
4575 * or keep old DACL if any
4577 if ((selection & DACL_SECURITY_INFORMATION) ?
4578 newhead->dacl : oldhead->dacl) {
4579 if (selection & DACL_SECURITY_INFORMATION) {
4580 offdacl = le32_to_cpu(newhead->dacl);
4581 pdacl = (const ACL*)&newattr[offdacl];
4582 } else {
4583 offdacl = le32_to_cpu(oldhead->dacl);
4584 pdacl = (const ACL*)&oldattr[offdacl];
4586 size = le16_to_cpu(pdacl->size);
4587 memcpy(&target[pos], pdacl, size);
4588 targhead->dacl = cpu_to_le32(pos);
4589 pos += size;
4590 } else
4591 targhead->dacl = const_cpu_to_le32(0);
4592 if (selection & DACL_SECURITY_INFORMATION) {
4593 control |= newhead->control
4594 & (SE_DACL_PRESENT
4595 | SE_DACL_DEFAULTED
4596 | SE_DACL_PROTECTED);
4597 if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
4598 control |= SE_DACL_AUTO_INHERITED;
4599 } else
4600 control |= oldhead->control
4601 & (SE_DACL_PRESENT
4602 | SE_DACL_DEFAULTED
4603 | SE_DACL_AUTO_INHERITED
4604 | SE_DACL_PROTECTED);
4606 * copy new SACL if selected
4607 * or keep old SACL if any
4609 if ((selection & SACL_SECURITY_INFORMATION) ?
4610 newhead->sacl : oldhead->sacl) {
4611 if (selection & SACL_SECURITY_INFORMATION) {
4612 offsacl = le32_to_cpu(newhead->sacl);
4613 psacl = (const ACL*)&newattr[offsacl];
4614 } else {
4615 offsacl = le32_to_cpu(oldhead->sacl);
4616 psacl = (const ACL*)&oldattr[offsacl];
4618 size = le16_to_cpu(psacl->size);
4619 memcpy(&target[pos], psacl, size);
4620 targhead->sacl = cpu_to_le32(pos);
4621 pos += size;
4622 } else
4623 targhead->sacl = const_cpu_to_le32(0);
4624 if (selection & SACL_SECURITY_INFORMATION) {
4625 control |= newhead->control
4626 & (SE_SACL_PRESENT
4627 | SE_SACL_DEFAULTED
4628 | SE_SACL_PROTECTED);
4629 if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
4630 control |= SE_SACL_AUTO_INHERITED;
4631 } else
4632 control |= oldhead->control
4633 & (SE_SACL_PRESENT
4634 | SE_SACL_DEFAULTED
4635 | SE_SACL_AUTO_INHERITED
4636 | SE_SACL_PROTECTED);
4638 * copy new OWNER if selected
4639 * or keep old OWNER if any
4641 if ((selection & OWNER_SECURITY_INFORMATION) ?
4642 newhead->owner : oldhead->owner) {
4643 if (selection & OWNER_SECURITY_INFORMATION) {
4644 offowner = le32_to_cpu(newhead->owner);
4645 powner = (const SID*)&newattr[offowner];
4646 } else {
4647 offowner = le32_to_cpu(oldhead->owner);
4648 powner = (const SID*)&oldattr[offowner];
4650 size = ntfs_sid_size(powner);
4651 memcpy(&target[pos], powner, size);
4652 targhead->owner = cpu_to_le32(pos);
4653 pos += size;
4654 } else
4655 targhead->owner = const_cpu_to_le32(0);
4656 if (selection & OWNER_SECURITY_INFORMATION)
4657 control |= newhead->control & SE_OWNER_DEFAULTED;
4658 else
4659 control |= oldhead->control & SE_OWNER_DEFAULTED;
4661 * copy new GROUP if selected
4662 * or keep old GROUP if any
4664 if ((selection & GROUP_SECURITY_INFORMATION) ?
4665 newhead->group : oldhead->group) {
4666 if (selection & GROUP_SECURITY_INFORMATION) {
4667 offgroup = le32_to_cpu(newhead->group);
4668 pgroup = (const SID*)&newattr[offgroup];
4669 control |= newhead->control
4670 & SE_GROUP_DEFAULTED;
4671 } else {
4672 offgroup = le32_to_cpu(oldhead->group);
4673 pgroup = (const SID*)&oldattr[offgroup];
4674 control |= oldhead->control
4675 & SE_GROUP_DEFAULTED;
4677 size = ntfs_sid_size(pgroup);
4678 memcpy(&target[pos], pgroup, size);
4679 targhead->group = cpu_to_le32(pos);
4680 pos += size;
4681 } else
4682 targhead->group = const_cpu_to_le32(0);
4683 if (selection & GROUP_SECURITY_INFORMATION)
4684 control |= newhead->control & SE_GROUP_DEFAULTED;
4685 else
4686 control |= oldhead->control & SE_GROUP_DEFAULTED;
4687 targhead->revision = SECURITY_DESCRIPTOR_REVISION;
4688 targhead->alignment = 0;
4689 targhead->control = control;
4690 ok = !update_secur_descr(vol, target, ni);
4691 free(target);
4693 return (ok);
4697 * Return the security descriptor of a file
4698 * This is intended to be similar to GetFileSecurity() from Win32
4699 * in order to facilitate the development of portable tools
4701 * returns zero if unsuccessful (following Win32 conventions)
4702 * -1 if no securid
4703 * the securid if any
4705 * The Win32 API is :
4707 * BOOL WINAPI GetFileSecurity(
4708 * __in LPCTSTR lpFileName,
4709 * __in SECURITY_INFORMATION RequestedInformation,
4710 * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor,
4711 * __in DWORD nLength,
4712 * __out LPDWORD lpnLengthNeeded
4713 * );
4717 int ntfs_get_file_security(struct SECURITY_API *scapi,
4718 const char *path, u32 selection,
4719 char *buf, u32 buflen, u32 *psize)
4721 ntfs_inode *ni;
4722 char *attr;
4723 int res;
4725 res = 0; /* default return */
4726 if (scapi && (scapi->magic == MAGIC_API)) {
4727 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4728 if (ni) {
4729 attr = getsecurityattr(scapi->security.vol, ni);
4730 if (attr) {
4731 if (feedsecurityattr(attr,selection,
4732 buf,buflen,psize)) {
4733 if (test_nino_flag(ni, v3_Extensions)
4734 && ni->security_id)
4735 res = le32_to_cpu(
4736 ni->security_id);
4737 else
4738 res = -1;
4740 free(attr);
4742 ntfs_inode_close(ni);
4743 } else
4744 errno = ENOENT;
4745 if (!res) *psize = 0;
4746 } else
4747 errno = EINVAL; /* do not clear *psize */
4748 return (res);
4753 * Set the security descriptor of a file or directory
4754 * This is intended to be similar to SetFileSecurity() from Win32
4755 * in order to facilitate the development of portable tools
4757 * returns zero if unsuccessful (following Win32 conventions)
4758 * -1 if no securid
4759 * the securid if any
4761 * The Win32 API is :
4763 * BOOL WINAPI SetFileSecurity(
4764 * __in LPCTSTR lpFileName,
4765 * __in SECURITY_INFORMATION SecurityInformation,
4766 * __in PSECURITY_DESCRIPTOR pSecurityDescriptor
4767 * );
4770 int ntfs_set_file_security(struct SECURITY_API *scapi,
4771 const char *path, u32 selection, const char *attr)
4773 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4774 ntfs_inode *ni;
4775 int attrsz;
4776 BOOL missing;
4777 char *oldattr;
4778 int res;
4780 res = 0; /* default return */
4781 if (scapi && (scapi->magic == MAGIC_API) && attr) {
4782 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4783 attrsz = ntfs_attr_size(attr);
4784 /* if selected, owner and group must be present or defaulted */
4785 missing = ((selection & OWNER_SECURITY_INFORMATION)
4786 && !phead->owner
4787 && !(phead->control & SE_OWNER_DEFAULTED))
4788 || ((selection & GROUP_SECURITY_INFORMATION)
4789 && !phead->group
4790 && !(phead->control & SE_GROUP_DEFAULTED));
4791 if (!missing
4792 && (phead->control & SE_SELF_RELATIVE)
4793 && ntfs_valid_descr(attr, attrsz)) {
4794 ni = ntfs_pathname_to_inode(scapi->security.vol,
4795 NULL, path);
4796 if (ni) {
4797 oldattr = getsecurityattr(scapi->security.vol,
4798 ni);
4799 if (oldattr) {
4800 if (mergesecurityattr(
4801 scapi->security.vol,
4802 oldattr, attr,
4803 selection, ni)) {
4804 if (test_nino_flag(ni,
4805 v3_Extensions))
4806 res = le32_to_cpu(
4807 ni->security_id);
4808 else
4809 res = -1;
4811 free(oldattr);
4813 ntfs_inode_close(ni);
4815 } else
4816 errno = EINVAL;
4817 } else
4818 errno = EINVAL;
4819 return (res);
4824 * Return the attributes of a file
4825 * This is intended to be similar to GetFileAttributes() from Win32
4826 * in order to facilitate the development of portable tools
4828 * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES)
4830 * The Win32 API is :
4832 * DWORD WINAPI GetFileAttributes(
4833 * __in LPCTSTR lpFileName
4834 * );
4837 int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
4839 ntfs_inode *ni;
4840 s32 attrib;
4842 attrib = -1; /* default return */
4843 if (scapi && (scapi->magic == MAGIC_API) && path) {
4844 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4845 if (ni) {
4846 attrib = le32_to_cpu(ni->flags);
4847 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4848 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4849 else
4850 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4851 if (!attrib)
4852 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4854 ntfs_inode_close(ni);
4855 } else
4856 errno = ENOENT;
4857 } else
4858 errno = EINVAL; /* do not clear *psize */
4859 return (attrib);
4864 * Set attributes to a file or directory
4865 * This is intended to be similar to SetFileAttributes() from Win32
4866 * in order to facilitate the development of portable tools
4868 * Only a few flags can be set (same list as Win32)
4870 * returns zero if unsuccessful (following Win32 conventions)
4871 * nonzero if successful
4873 * The Win32 API is :
4875 * BOOL WINAPI SetFileAttributes(
4876 * __in LPCTSTR lpFileName,
4877 * __in DWORD dwFileAttributes
4878 * );
4881 BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
4882 const char *path, s32 attrib)
4884 ntfs_inode *ni;
4885 le32 settable;
4886 ATTR_FLAGS dirflags;
4887 int res;
4889 res = 0; /* default return */
4890 if (scapi && (scapi->magic == MAGIC_API) && path) {
4891 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4892 if (ni) {
4893 settable = FILE_ATTR_SETTABLE;
4894 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4896 * Accept changing compression for a directory
4897 * and set index root accordingly
4899 settable |= FILE_ATTR_COMPRESSED;
4900 if ((ni->flags ^ cpu_to_le32(attrib))
4901 & FILE_ATTR_COMPRESSED) {
4902 if (ni->flags & FILE_ATTR_COMPRESSED)
4903 dirflags = const_cpu_to_le16(0);
4904 else
4905 dirflags = ATTR_IS_COMPRESSED;
4906 res = ntfs_attr_set_flags(ni,
4907 AT_INDEX_ROOT,
4908 NTFS_INDEX_I30, 4,
4909 dirflags,
4910 ATTR_COMPRESSION_MASK);
4913 if (!res) {
4914 ni->flags = (ni->flags & ~settable)
4915 | (cpu_to_le32(attrib) & settable);
4916 NInoSetDirty(ni);
4917 NInoFileNameSetDirty(ni);
4919 if (!ntfs_inode_close(ni))
4920 res = -1;
4921 } else
4922 errno = ENOENT;
4924 return (res);
4928 BOOL ntfs_read_directory(struct SECURITY_API *scapi,
4929 const char *path, ntfs_filldir_t callback, void *context)
4931 ntfs_inode *ni;
4932 BOOL ok;
4933 s64 pos;
4935 ok = FALSE; /* default return */
4936 if (scapi && (scapi->magic == MAGIC_API) && callback) {
4937 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4938 if (ni) {
4939 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4940 pos = 0;
4941 ntfs_readdir(ni,&pos,context,callback);
4942 ok = !ntfs_inode_close(ni);
4943 } else {
4944 ntfs_inode_close(ni);
4945 errno = ENOTDIR;
4947 } else
4948 errno = ENOENT;
4949 } else
4950 errno = EINVAL; /* do not clear *psize */
4951 return (ok);
4955 * read $SDS (for auditing security data)
4957 * Returns the number or read bytes, or -1 if there is an error
4960 int ntfs_read_sds(struct SECURITY_API *scapi,
4961 char *buf, u32 size, u32 offset)
4963 int got;
4965 got = -1; /* default return */
4966 if (scapi && (scapi->magic == MAGIC_API)) {
4967 if (scapi->security.vol->secure_ni)
4968 got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
4969 STREAM_SDS, 4, buf, size, offset);
4970 else
4971 errno = EOPNOTSUPP;
4972 } else
4973 errno = EINVAL;
4974 return (got);
4978 * read $SII (for auditing security data)
4980 * Returns next entry, or NULL if there is an error
4983 INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
4984 INDEX_ENTRY *entry)
4986 SII_INDEX_KEY key;
4987 INDEX_ENTRY *ret;
4988 BOOL found;
4989 ntfs_index_context *xsii;
4991 ret = (INDEX_ENTRY*)NULL; /* default return */
4992 if (scapi && (scapi->magic == MAGIC_API)) {
4993 xsii = scapi->security.vol->secure_xsii;
4994 if (xsii) {
4995 if (!entry) {
4996 key.security_id = const_cpu_to_le32(0);
4997 found = !ntfs_index_lookup((char*)&key,
4998 sizeof(SII_INDEX_KEY), xsii);
4999 /* not supposed to find */
5000 if (!found && (errno == ENOENT))
5001 ret = xsii->entry;
5002 } else
5003 ret = ntfs_index_next(entry,xsii);
5004 if (!ret)
5005 errno = ENODATA;
5006 } else
5007 errno = EOPNOTSUPP;
5008 } else
5009 errno = EINVAL;
5010 return (ret);
5014 * read $SDH (for auditing security data)
5016 * Returns next entry, or NULL if there is an error
5019 INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
5020 INDEX_ENTRY *entry)
5022 SDH_INDEX_KEY key;
5023 INDEX_ENTRY *ret;
5024 BOOL found;
5025 ntfs_index_context *xsdh;
5027 ret = (INDEX_ENTRY*)NULL; /* default return */
5028 if (scapi && (scapi->magic == MAGIC_API)) {
5029 xsdh = scapi->security.vol->secure_xsdh;
5030 if (xsdh) {
5031 if (!entry) {
5032 key.hash = const_cpu_to_le32(0);
5033 key.security_id = const_cpu_to_le32(0);
5034 found = !ntfs_index_lookup((char*)&key,
5035 sizeof(SDH_INDEX_KEY), xsdh);
5036 /* not supposed to find */
5037 if (!found && (errno == ENOENT))
5038 ret = xsdh->entry;
5039 } else
5040 ret = ntfs_index_next(entry,xsdh);
5041 if (!ret)
5042 errno = ENODATA;
5043 } else errno = ENOTSUP;
5044 } else
5045 errno = EINVAL;
5046 return (ret);
5050 * Get the mapped user SID
5051 * A buffer of 40 bytes has to be supplied
5053 * returns the size of the SID, or zero and errno set if not found
5056 int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
5058 const SID *usid;
5059 BIGSID defusid;
5060 int size;
5062 size = 0;
5063 if (scapi && (scapi->magic == MAGIC_API)) {
5064 usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
5065 if (usid) {
5066 size = ntfs_sid_size(usid);
5067 memcpy(buf,usid,size);
5068 } else
5069 errno = ENODATA;
5070 } else
5071 errno = EINVAL;
5072 return (size);
5076 * Get the mapped group SID
5077 * A buffer of 40 bytes has to be supplied
5079 * returns the size of the SID, or zero and errno set if not found
5082 int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
5084 const SID *gsid;
5085 BIGSID defgsid;
5086 int size;
5088 size = 0;
5089 if (scapi && (scapi->magic == MAGIC_API)) {
5090 gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
5091 if (gsid) {
5092 size = ntfs_sid_size(gsid);
5093 memcpy(buf,gsid,size);
5094 } else
5095 errno = ENODATA;
5096 } else
5097 errno = EINVAL;
5098 return (size);
5102 * Get the user mapped to a SID
5104 * returns the uid, or -1 if not found
5107 int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
5109 int uid;
5111 uid = -1;
5112 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
5113 if (ntfs_same_sid(usid,adminsid))
5114 uid = 0;
5115 else {
5116 uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
5117 if (!uid) {
5118 uid = -1;
5119 errno = ENODATA;
5122 } else
5123 errno = EINVAL;
5124 return (uid);
5128 * Get the group mapped to a SID
5130 * returns the uid, or -1 if not found
5133 int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
5135 int gid;
5137 gid = -1;
5138 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
5139 if (ntfs_same_sid(gsid,adminsid))
5140 gid = 0;
5141 else {
5142 gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
5143 if (!gid) {
5144 gid = -1;
5145 errno = ENODATA;
5148 } else
5149 errno = EINVAL;
5150 return (gid);
5154 * Initializations before calling ntfs_get_file_security()
5155 * ntfs_set_file_security() and ntfs_read_directory()
5157 * Only allowed for root
5159 * Returns an (obscured) struct SECURITY_API* needed for further calls
5160 * NULL if not root (EPERM) or device is mounted (EBUSY)
5163 struct SECURITY_API *ntfs_initialize_file_security(const char *device,
5164 unsigned long flags)
5166 ntfs_volume *vol;
5167 unsigned long mntflag;
5168 int mnt;
5169 struct SECURITY_API *scapi;
5170 struct SECURITY_CONTEXT *scx;
5172 scapi = (struct SECURITY_API*)NULL;
5173 mnt = ntfs_check_if_mounted(device, &mntflag);
5174 if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
5175 vol = ntfs_mount(device, flags);
5176 if (vol) {
5177 scapi = (struct SECURITY_API*)
5178 ntfs_malloc(sizeof(struct SECURITY_API));
5179 if (!ntfs_volume_get_free_space(vol)
5180 && scapi) {
5181 scapi->magic = MAGIC_API;
5182 scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
5183 scx = &scapi->security;
5184 scx->vol = vol;
5185 scx->uid = getuid();
5186 scx->gid = getgid();
5187 scx->pseccache = &scapi->seccache;
5188 scx->vol->secure_flags = 0;
5189 /* accept no mapping and no $Secure */
5190 ntfs_build_mapping(scx,(const char*)NULL,TRUE);
5191 ntfs_open_secure(vol);
5192 } else {
5193 if (scapi)
5194 free(scapi);
5195 else
5196 errno = ENOMEM;
5197 mnt = ntfs_umount(vol,FALSE);
5198 scapi = (struct SECURITY_API*)NULL;
5201 } else
5202 if (getuid())
5203 errno = EPERM;
5204 else
5205 errno = EBUSY;
5206 return (scapi);
5210 * Leaving after ntfs_initialize_file_security()
5212 * Returns FALSE if FAILED
5215 BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
5217 int ok;
5218 ntfs_volume *vol;
5220 ok = FALSE;
5221 if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
5222 vol = scapi->security.vol;
5223 ntfs_close_secure(&scapi->security);
5224 free(scapi);
5225 if (!ntfs_umount(vol, 0))
5226 ok = TRUE;
5228 return (ok);