blob: e00bcf9579c39c71c3d630b8f5ba644317b13aa2 [file] [log] [blame]
Steve Kondik2111ad72013-07-07 12:07:44 -07001/**
2 * security.c - Handling security/ACLs in NTFS. Originated from the Linux-NTFS project.
3 *
4 * Copyright (c) 2004 Anton Altaparmakov
5 * Copyright (c) 2005-2006 Szabolcs Szakacsits
6 * Copyright (c) 2006 Yura Pakhuchiy
Steve Kondike68cb602016-08-28 00:45:36 -07007 * Copyright (c) 2007-2015 Jean-Pierre Andre
Steve Kondik2111ad72013-07-07 12:07:44 -07008 *
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.
13 *
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.
18 *
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
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
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
50
51#include <unistd.h>
52#include <pwd.h>
53#include <grp.h>
54
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"
67
68/*
69 * JPA NTFS constants or structs
70 * should be moved to layout.h
71 */
72
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 */
77
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 )
86
87struct 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;
96
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;
103} ;
104
105struct 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;
115
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;
123 } ;
124
125/*
126 * A few useful constants
127 */
128
129static 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) };
134static 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) };
139
140/*
141 * null SID (S-1-0-0)
142 */
143
144extern const SID *nullsid;
145
146/*
147 * The zero GUID.
148 */
149
150static 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 } };
152static const GUID *const zero_guid = &__zero_guid;
153
154/**
155 * ntfs_guid_is_zero - check if a GUID is zero
156 * @guid: [IN] guid to check
157 *
158 * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
159 * and FALSE otherwise.
160 */
161BOOL ntfs_guid_is_zero(const GUID *guid)
162{
163 return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
164}
165
166/**
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)
170 *
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.
174 *
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.
178 *
179 * On success return the converted string and on failure return NULL with errno
180 * set to the error code.
181 */
182char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
183{
184 char *_guid_str;
185 int res;
186
187 if (!guid) {
188 errno = EINVAL;
189 return NULL;
190 }
191 _guid_str = guid_str;
192 if (!_guid_str) {
193 _guid_str = (char*)ntfs_malloc(37);
194 if (!_guid_str)
195 return _guid_str;
196 }
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;
210}
211
212/**
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
215 *
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.
219 *
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.
222 */
223int ntfs_sid_to_mbs_size(const SID *sid)
224{
225 int size, i;
226
Steve Kondike68cb602016-08-28 00:45:36 -0700227 if (!ntfs_valid_sid(sid)) {
Steve Kondik2111ad72013-07-07 12:07:44 -0700228 errno = EINVAL;
229 return -1;
230 }
231 /* Start with "S-". */
232 size = 2;
233 /*
234 * Add the SID_REVISION. Hopefully the compiler will optimize this
235 * away as SID_REVISION is a constant.
236 */
237 for (i = SID_REVISION; i > 0; i /= 10)
238 size++;
239 /* Add the "-". */
240 size++;
241 /*
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.
245 */
246 if (!sid->identifier_authority.high_part)
247 size += 10;
248 else
249 size += 14;
250 /*
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.
253 */
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);
258}
259
260/**
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
265 *
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.
270 *
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.
282 *
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.
286 *
287 * On success return the converted string and on failure return NULL with errno
288 * set to the error code.
289 */
290char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
291{
292 u64 u;
293 le32 leauth;
294 char *s;
295 int i, j, cnt;
296
297 /*
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.
300 */
Steve Kondike68cb602016-08-28 00:45:36 -0700301 if (sid_str && (sid_str_size < 8 || !ntfs_valid_sid(sid))) {
Steve Kondik2111ad72013-07-07 12:07:44 -0700302 errno = EINVAL;
303 return NULL;
304 }
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;
319 }
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;
346 }
347 return sid_str;
348err_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;
357}
358
359/**
360 * ntfs_generate_guid - generatates a random current guid.
361 * @guid: [OUT] pointer to a GUID struct to hold the generated guid.
362 *
363 * perhaps not a very good random number generator though...
364 */
365void ntfs_generate_guid(GUID *guid)
366{
367 unsigned int i;
368 u8 *p = (u8 *)guid;
369
Steve Kondik79165c32015-11-09 19:43:00 -0800370 /* this is called at most once from mkntfs */
371 srandom(time((time_t*)NULL) ^ (getpid() << 16));
Steve Kondik2111ad72013-07-07 12:07:44 -0700372 for (i = 0; i < sizeof(GUID); i++) {
373 p[i] = (u8)(random() & 0xFF);
374 if (i == 7)
375 p[7] = (p[7] & 0x0F) | 0x40;
376 if (i == 8)
377 p[8] = (p[8] & 0x3F) | 0x80;
378 }
379}
380
381/**
382 * ntfs_security_hash - calculate the hash of a security descriptor
383 * @sd: self-relative security descriptor whose hash to calculate
384 * @length: size in bytes of the security descritor @sd
385 *
386 * Calculate the hash of the self-relative security descriptor @sd of length
387 * @length bytes.
388 *
389 * This hash is used in the $Secure system file as the primary key for the $SDH
390 * index and is also stored in the header of each security descriptor in the
391 * $SDS data stream as well as in the index data of both the $SII and $SDH
392 * indexes. In all three cases it forms part of the SDS_ENTRY_HEADER
393 * structure.
394 *
395 * Return the calculated security hash in little endian.
396 */
397le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len)
398{
399 const le32 *pos = (const le32*)sd;
400 const le32 *end = pos + (len >> 2);
401 u32 hash = 0;
402
403 while (pos < end) {
404 hash = le32_to_cpup(pos) + ntfs_rol32(hash, 3);
405 pos++;
406 }
407 return cpu_to_le32(hash);
408}
409
410/*
411 * Get the first entry of current index block
412 * cut and pasted form ntfs_ie_get_first() in index.c
413 */
414
415static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
416{
417 return (INDEX_ENTRY*)((u8*)ih + le32_to_cpu(ih->entries_offset));
418}
419
420/*
421 * Stuff a 256KB block into $SDS before writing descriptors
422 * into the block.
423 *
424 * This prevents $SDS from being automatically declared as sparse
425 * when the second copy of the first security descriptor is written
426 * 256KB further ahead.
427 *
428 * Having $SDS declared as a sparse file is not wrong by itself
429 * and chkdsk leaves it as a sparse file. It does however complain
430 * and add a sparse flag (0x0200) into field file_attributes of
431 * STANDARD_INFORMATION of $Secure. This probably means that a
432 * sparse attribute (ATTR_IS_SPARSE) is only allowed in sparse
433 * files (FILE_ATTR_SPARSE_FILE).
434 *
435 * Windows normally does not convert to sparse attribute or sparse
436 * file. Stuffing is just a way to get to the same result.
437 */
438
439static int entersecurity_stuff(ntfs_volume *vol, off_t offs)
440{
441 int res;
442 int written;
443 unsigned long total;
444 char *stuff;
445
446 res = 0;
447 total = 0;
448 stuff = (char*)ntfs_malloc(STUFFSZ);
449 if (stuff) {
450 memset(stuff, 0, STUFFSZ);
451 do {
452 written = ntfs_attr_data_write(vol->secure_ni,
453 STREAM_SDS, 4, stuff, STUFFSZ, offs);
454 if (written == STUFFSZ) {
455 total += STUFFSZ;
456 offs += STUFFSZ;
457 } else {
458 errno = ENOSPC;
459 res = -1;
460 }
461 } while (!res && (total < ALIGN_SDS_BLOCK));
462 free(stuff);
463 } else {
464 errno = ENOMEM;
465 res = -1;
466 }
467 return (res);
468}
469
470/*
471 * Enter a new security descriptor into $Secure (data only)
472 * it has to be written twice with an offset of 256KB
473 *
474 * Should only be called by entersecurityattr() to ensure consistency
475 *
476 * Returns zero if sucessful
477 */
478
479static int entersecurity_data(ntfs_volume *vol,
480 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
481 le32 hash, le32 keyid, off_t offs, int gap)
482{
483 int res;
484 int written1;
485 int written2;
486 char *fullattr;
487 int fullsz;
488 SECURITY_DESCRIPTOR_HEADER *phsds;
489
490 res = -1;
491 fullsz = attrsz + gap + sizeof(SECURITY_DESCRIPTOR_HEADER);
492 fullattr = (char*)ntfs_malloc(fullsz);
493 if (fullattr) {
494 /*
495 * Clear the gap from previous descriptor
496 * this could be useful for appending the second
497 * copy to the end of file. When creating a new
498 * 256K block, the gap is cleared while writing
499 * the first copy
500 */
501 if (gap)
502 memset(fullattr,0,gap);
503 memcpy(&fullattr[gap + sizeof(SECURITY_DESCRIPTOR_HEADER)],
504 attr,attrsz);
505 phsds = (SECURITY_DESCRIPTOR_HEADER*)&fullattr[gap];
506 phsds->hash = hash;
507 phsds->security_id = keyid;
508 phsds->offset = cpu_to_le64(offs);
509 phsds->length = cpu_to_le32(fullsz - gap);
510 written1 = ntfs_attr_data_write(vol->secure_ni,
511 STREAM_SDS, 4, fullattr, fullsz,
512 offs - gap);
513 written2 = ntfs_attr_data_write(vol->secure_ni,
514 STREAM_SDS, 4, fullattr, fullsz,
515 offs - gap + ALIGN_SDS_BLOCK);
516 if ((written1 == fullsz)
Steve Kondik79165c32015-11-09 19:43:00 -0800517 && (written2 == written1)) {
518 /*
519 * Make sure the data size for $SDS marks the end
520 * of the last security attribute. Windows uses
521 * this to determine where the next attribute will
522 * be written, which causes issues if chkdsk had
523 * previously deleted the last entries without
524 * adjusting the size.
525 */
526 res = ntfs_attr_shrink_size(vol->secure_ni,STREAM_SDS,
527 4, offs - gap + ALIGN_SDS_BLOCK + fullsz);
528 }
Steve Kondik2111ad72013-07-07 12:07:44 -0700529 else
530 errno = ENOSPC;
531 free(fullattr);
532 } else
533 errno = ENOMEM;
534 return (res);
535}
536
537/*
538 * Enter a new security descriptor in $Secure (indexes only)
539 *
540 * Should only be called by entersecurityattr() to ensure consistency
541 *
542 * Returns zero if sucessful
543 */
544
545static int entersecurity_indexes(ntfs_volume *vol, s64 attrsz,
546 le32 hash, le32 keyid, off_t offs)
547{
548 union {
549 struct {
550 le32 dataoffsl;
551 le32 dataoffsh;
552 } parts;
553 le64 all;
554 } realign;
555 int res;
556 ntfs_index_context *xsii;
557 ntfs_index_context *xsdh;
558 struct SII newsii;
559 struct SDH newsdh;
560
561 res = -1;
562 /* enter a new $SII record */
563
564 xsii = vol->secure_xsii;
565 ntfs_index_ctx_reinit(xsii);
566 newsii.offs = const_cpu_to_le16(20);
567 newsii.size = const_cpu_to_le16(sizeof(struct SII) - 20);
568 newsii.fill1 = const_cpu_to_le32(0);
569 newsii.indexsz = const_cpu_to_le16(sizeof(struct SII));
570 newsii.indexksz = const_cpu_to_le16(sizeof(SII_INDEX_KEY));
571 newsii.flags = const_cpu_to_le16(0);
572 newsii.fill2 = const_cpu_to_le16(0);
573 newsii.keysecurid = keyid;
574 newsii.hash = hash;
575 newsii.securid = keyid;
576 realign.all = cpu_to_le64(offs);
577 newsii.dataoffsh = realign.parts.dataoffsh;
578 newsii.dataoffsl = realign.parts.dataoffsl;
579 newsii.datasize = cpu_to_le32(attrsz
580 + sizeof(SECURITY_DESCRIPTOR_HEADER));
581 if (!ntfs_ie_add(xsii,(INDEX_ENTRY*)&newsii)) {
582
583 /* enter a new $SDH record */
584
585 xsdh = vol->secure_xsdh;
586 ntfs_index_ctx_reinit(xsdh);
587 newsdh.offs = const_cpu_to_le16(24);
588 newsdh.size = const_cpu_to_le16(
589 sizeof(SECURITY_DESCRIPTOR_HEADER));
590 newsdh.fill1 = const_cpu_to_le32(0);
591 newsdh.indexsz = const_cpu_to_le16(
592 sizeof(struct SDH));
593 newsdh.indexksz = const_cpu_to_le16(
594 sizeof(SDH_INDEX_KEY));
595 newsdh.flags = const_cpu_to_le16(0);
596 newsdh.fill2 = const_cpu_to_le16(0);
597 newsdh.keyhash = hash;
598 newsdh.keysecurid = keyid;
599 newsdh.hash = hash;
600 newsdh.securid = keyid;
601 newsdh.dataoffsh = realign.parts.dataoffsh;
602 newsdh.dataoffsl = realign.parts.dataoffsl;
603 newsdh.datasize = cpu_to_le32(attrsz
604 + sizeof(SECURITY_DESCRIPTOR_HEADER));
605 /* special filler value, Windows generally */
606 /* fills with 0x00490049, sometimes with zero */
607 newsdh.fill3 = const_cpu_to_le32(0x00490049);
608 if (!ntfs_ie_add(xsdh,(INDEX_ENTRY*)&newsdh))
609 res = 0;
610 }
611 return (res);
612}
613
614/*
615 * Enter a new security descriptor in $Secure (data and indexes)
616 * Returns id of entry, or zero if there is a problem.
617 * (should not be called for NTFS version < 3.0)
618 *
619 * important : calls have to be serialized, however no locking is
620 * needed while fuse is not multithreaded
621 */
622
623static le32 entersecurityattr(ntfs_volume *vol,
624 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz,
625 le32 hash)
626{
627 union {
628 struct {
629 le32 dataoffsl;
630 le32 dataoffsh;
631 } parts;
632 le64 all;
633 } realign;
634 le32 securid;
635 le32 keyid;
636 u32 newkey;
637 off_t offs;
638 int gap;
639 int size;
640 BOOL found;
641 struct SII *psii;
642 INDEX_ENTRY *entry;
643 INDEX_ENTRY *next;
644 ntfs_index_context *xsii;
645 int retries;
646 ntfs_attr *na;
647 int olderrno;
648
649 /* find the first available securid beyond the last key */
650 /* in $Secure:$SII. This also determines the first */
651 /* available location in $Secure:$SDS, as this stream */
652 /* is always appended to and the id's are allocated */
653 /* in sequence */
654
655 securid = const_cpu_to_le32(0);
656 xsii = vol->secure_xsii;
657 ntfs_index_ctx_reinit(xsii);
658 offs = size = 0;
659 keyid = const_cpu_to_le32(-1);
660 olderrno = errno;
661 found = !ntfs_index_lookup((char*)&keyid,
662 sizeof(SII_INDEX_KEY), xsii);
663 if (!found && (errno != ENOENT)) {
664 ntfs_log_perror("Inconsistency in index $SII");
665 psii = (struct SII*)NULL;
666 } else {
667 /* restore errno to avoid misinterpretation */
668 errno = olderrno;
669 entry = xsii->entry;
670 psii = (struct SII*)xsii->entry;
671 }
672 if (psii) {
673 /*
674 * Get last entry in block, but must get first one
675 * one first, as we should already be beyond the
676 * last one. For some reason the search for the last
677 * entry sometimes does not return the last block...
678 * we assume this can only happen in root block
679 */
680 if (xsii->is_in_root)
681 entry = ntfs_ie_get_first
682 ((INDEX_HEADER*)&xsii->ir->index);
683 else
684 entry = ntfs_ie_get_first
685 ((INDEX_HEADER*)&xsii->ib->index);
686 /*
687 * All index blocks should be at least half full
688 * so there always is a last entry but one,
689 * except when creating the first entry in index root.
690 * This was however found not to be true : chkdsk
691 * sometimes deletes all the (unused) keys in the last
692 * index block without rebalancing the tree.
693 * When this happens, a new search is restarted from
694 * the smallest key.
695 */
696 keyid = const_cpu_to_le32(0);
697 retries = 0;
698 while (entry) {
699 next = ntfs_index_next(entry,xsii);
700 if (next) {
701 psii = (struct SII*)next;
702 /* save last key and */
703 /* available position */
704 keyid = psii->keysecurid;
705 realign.parts.dataoffsh
706 = psii->dataoffsh;
707 realign.parts.dataoffsl
708 = psii->dataoffsl;
709 offs = le64_to_cpu(realign.all);
710 size = le32_to_cpu(psii->datasize);
711 }
712 entry = next;
713 if (!entry && !keyid && !retries) {
714 /* search failed, retry from smallest key */
715 ntfs_index_ctx_reinit(xsii);
716 found = !ntfs_index_lookup((char*)&keyid,
717 sizeof(SII_INDEX_KEY), xsii);
718 if (!found && (errno != ENOENT)) {
719 ntfs_log_perror("Index $SII is broken");
Steve Kondik79165c32015-11-09 19:43:00 -0800720 psii = (struct SII*)NULL;
Steve Kondik2111ad72013-07-07 12:07:44 -0700721 } else {
722 /* restore errno */
723 errno = olderrno;
724 entry = xsii->entry;
Steve Kondik79165c32015-11-09 19:43:00 -0800725 psii = (struct SII*)entry;
726 }
727 if (psii
728 && !(psii->flags & INDEX_ENTRY_END)) {
729 /* save first key and */
730 /* available position */
731 keyid = psii->keysecurid;
732 realign.parts.dataoffsh
733 = psii->dataoffsh;
734 realign.parts.dataoffsl
735 = psii->dataoffsl;
736 offs = le64_to_cpu(realign.all);
737 size = le32_to_cpu(psii->datasize);
Steve Kondik2111ad72013-07-07 12:07:44 -0700738 }
739 retries++;
740 }
741 }
742 }
743 if (!keyid) {
744 /*
745 * could not find any entry, before creating the first
746 * entry, make a double check by making sure size of $SII
747 * is less than needed for one entry
748 */
749 securid = const_cpu_to_le32(0);
750 na = ntfs_attr_open(vol->secure_ni,AT_INDEX_ROOT,sii_stream,4);
751 if (na) {
Steve Kondik79165c32015-11-09 19:43:00 -0800752 if ((size_t)na->data_size < (sizeof(struct SII)
753 + sizeof(INDEX_ENTRY_HEADER))) {
Steve Kondik2111ad72013-07-07 12:07:44 -0700754 ntfs_log_error("Creating the first security_id\n");
755 securid = const_cpu_to_le32(FIRST_SECURITY_ID);
756 }
757 ntfs_attr_close(na);
758 }
759 if (!securid) {
760 ntfs_log_error("Error creating a security_id\n");
761 errno = EIO;
762 }
763 } else {
764 newkey = le32_to_cpu(keyid) + 1;
765 securid = cpu_to_le32(newkey);
766 }
767 /*
768 * The security attr has to be written twice 256KB
769 * apart. This implies that offsets like
770 * 0x40000*odd_integer must be left available for
771 * the second copy. So align to next block when
772 * the last byte overflows on a wrong block.
773 */
774
775 if (securid) {
776 gap = (-size) & (ALIGN_SDS_ENTRY - 1);
777 offs += gap + size;
778 if ((offs + attrsz + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
779 & ALIGN_SDS_BLOCK) {
780 offs = ((offs + attrsz
781 + sizeof(SECURITY_DESCRIPTOR_HEADER) - 1)
782 | (ALIGN_SDS_BLOCK - 1)) + 1;
783 }
784 if (!(offs & (ALIGN_SDS_BLOCK - 1)))
785 entersecurity_stuff(vol, offs);
786 /*
787 * now write the security attr to storage :
788 * first data, then SII, then SDH
789 * If failure occurs while writing SDS, data will never
790 * be accessed through indexes, and will be overwritten
791 * by the next allocated descriptor
792 * If failure occurs while writing SII, the id has not
793 * recorded and will be reallocated later
794 * If failure occurs while writing SDH, the space allocated
795 * in SDS or SII will not be reused, an inconsistency
796 * will persist with no significant consequence
797 */
798 if (entersecurity_data(vol, attr, attrsz, hash, securid, offs, gap)
799 || entersecurity_indexes(vol, attrsz, hash, securid, offs))
800 securid = const_cpu_to_le32(0);
801 }
802 /* inode now is dirty, synchronize it all */
803 ntfs_index_entry_mark_dirty(vol->secure_xsii);
804 ntfs_index_ctx_reinit(vol->secure_xsii);
805 ntfs_index_entry_mark_dirty(vol->secure_xsdh);
806 ntfs_index_ctx_reinit(vol->secure_xsdh);
807 NInoSetDirty(vol->secure_ni);
808 if (ntfs_inode_sync(vol->secure_ni))
809 ntfs_log_perror("Could not sync $Secure\n");
810 return (securid);
811}
812
813/*
814 * Find a matching security descriptor in $Secure,
815 * if none, allocate a new id and write the descriptor to storage
816 * Returns id of entry, or zero if there is a problem.
817 *
818 * important : calls have to be serialized, however no locking is
819 * needed while fuse is not multithreaded
820 */
821
822static le32 setsecurityattr(ntfs_volume *vol,
823 const SECURITY_DESCRIPTOR_RELATIVE *attr, s64 attrsz)
824{
825 struct SDH *psdh; /* this is an image of index (le) */
826 union {
827 struct {
828 le32 dataoffsl;
829 le32 dataoffsh;
830 } parts;
831 le64 all;
832 } realign;
833 BOOL found;
834 BOOL collision;
835 size_t size;
836 size_t rdsize;
837 s64 offs;
838 int res;
839 ntfs_index_context *xsdh;
840 char *oldattr;
841 SDH_INDEX_KEY key;
842 INDEX_ENTRY *entry;
843 le32 securid;
844 le32 hash;
845 int olderrno;
846
847 hash = ntfs_security_hash(attr,attrsz);
848 oldattr = (char*)NULL;
849 securid = const_cpu_to_le32(0);
850 res = 0;
851 xsdh = vol->secure_xsdh;
852 if (vol->secure_ni && xsdh && !vol->secure_reentry++) {
853 ntfs_index_ctx_reinit(xsdh);
854 /*
855 * find the nearest key as (hash,0)
856 * (do not search for partial key : in case of collision,
857 * it could return a key which is not the first one which
858 * collides)
859 */
860 key.hash = hash;
861 key.security_id = const_cpu_to_le32(0);
862 olderrno = errno;
863 found = !ntfs_index_lookup((char*)&key,
864 sizeof(SDH_INDEX_KEY), xsdh);
865 if (!found && (errno != ENOENT))
866 ntfs_log_perror("Inconsistency in index $SDH");
867 else {
868 /* restore errno to avoid misinterpretation */
869 errno = olderrno;
870 entry = xsdh->entry;
871 found = FALSE;
872 /*
873 * lookup() may return a node with no data,
874 * if so get next
875 */
876 if (entry->ie_flags & INDEX_ENTRY_END)
877 entry = ntfs_index_next(entry,xsdh);
878 do {
879 collision = FALSE;
880 psdh = (struct SDH*)entry;
881 if (psdh)
882 size = (size_t) le32_to_cpu(psdh->datasize)
883 - sizeof(SECURITY_DESCRIPTOR_HEADER);
884 else size = 0;
885 /* if hash is not the same, the key is not present */
886 if (psdh && (size > 0)
887 && (psdh->keyhash == hash)) {
888 /* if hash is the same */
889 /* check the whole record */
890 realign.parts.dataoffsh = psdh->dataoffsh;
891 realign.parts.dataoffsl = psdh->dataoffsl;
892 offs = le64_to_cpu(realign.all)
893 + sizeof(SECURITY_DESCRIPTOR_HEADER);
894 oldattr = (char*)ntfs_malloc(size);
895 if (oldattr) {
896 rdsize = ntfs_attr_data_read(
897 vol->secure_ni,
898 STREAM_SDS, 4,
899 oldattr, size, offs);
900 found = (rdsize == size)
901 && !memcmp(oldattr,attr,size);
902 free(oldattr);
903 /* if the records do not compare */
904 /* (hash collision), try next one */
905 if (!found) {
906 entry = ntfs_index_next(
907 entry,xsdh);
908 collision = TRUE;
909 }
910 } else
911 res = ENOMEM;
912 }
913 } while (collision && entry);
914 if (found)
915 securid = psdh->keysecurid;
916 else {
917 if (res) {
918 errno = res;
919 securid = const_cpu_to_le32(0);
920 } else {
921 /*
922 * no matching key :
923 * have to build a new one
924 */
925 securid = entersecurityattr(vol,
926 attr, attrsz, hash);
927 }
928 }
929 }
930 }
931 if (--vol->secure_reentry)
932 ntfs_log_perror("Reentry error, check no multithreading\n");
933 return (securid);
934}
935
936
937/*
938 * Update the security descriptor of a file
939 * Either as an attribute (complying with pre v3.x NTFS version)
940 * or, when possible, as an entry in $Secure (for NTFS v3.x)
941 *
942 * returns 0 if success
943 */
944
945static int update_secur_descr(ntfs_volume *vol,
946 char *newattr, ntfs_inode *ni)
947{
948 int newattrsz;
949 int written;
950 int res;
951 ntfs_attr *na;
952
953 newattrsz = ntfs_attr_size(newattr);
954
955#if !FORCE_FORMAT_v1x
956 if ((vol->major_ver < 3) || !vol->secure_ni) {
957#endif
958
959 /* update for NTFS format v1.x */
960
961 /* update the old security attribute */
962 na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
963 if (na) {
964 /* resize attribute */
965 res = ntfs_attr_truncate(na, (s64) newattrsz);
966 /* overwrite value */
967 if (!res) {
968 written = (int)ntfs_attr_pwrite(na, (s64) 0,
969 (s64) newattrsz, newattr);
970 if (written != newattrsz) {
971 ntfs_log_error("Failed to update "
972 "a v1.x security descriptor\n");
973 errno = EIO;
974 res = -1;
975 }
976 }
977
978 ntfs_attr_close(na);
979 /* if old security attribute was found, also */
980 /* truncate standard information attribute to v1.x */
981 /* this is needed when security data is wanted */
982 /* as v1.x though volume is formatted for v3.x */
983 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
984 AT_UNNAMED, 0);
985 if (na) {
986 clear_nino_flag(ni, v3_Extensions);
987 /*
988 * Truncating the record does not sweep extensions
989 * from copy in memory. Clear security_id to be safe
990 */
991 ni->security_id = const_cpu_to_le32(0);
992 res = ntfs_attr_truncate(na, (s64)48);
993 ntfs_attr_close(na);
994 clear_nino_flag(ni, v3_Extensions);
995 }
996 } else {
997 /*
998 * insert the new security attribute if there
999 * were none
1000 */
1001 res = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR,
1002 AT_UNNAMED, 0, (u8*)newattr,
1003 (s64) newattrsz);
1004 }
1005#if !FORCE_FORMAT_v1x
1006 } else {
1007
1008 /* update for NTFS format v3.x */
1009
1010 le32 securid;
1011
1012 securid = setsecurityattr(vol,
1013 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
1014 (s64)newattrsz);
1015 if (securid) {
1016 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1017 AT_UNNAMED, 0);
1018 if (na) {
1019 res = 0;
1020 if (!test_nino_flag(ni, v3_Extensions)) {
1021 /* expand standard information attribute to v3.x */
1022 res = ntfs_attr_truncate(na,
1023 (s64)sizeof(STANDARD_INFORMATION));
1024 ni->owner_id = const_cpu_to_le32(0);
1025 ni->quota_charged = const_cpu_to_le64(0);
1026 ni->usn = const_cpu_to_le64(0);
1027 ntfs_attr_remove(ni,
1028 AT_SECURITY_DESCRIPTOR,
1029 AT_UNNAMED, 0);
1030 }
1031 set_nino_flag(ni, v3_Extensions);
1032 ni->security_id = securid;
1033 ntfs_attr_close(na);
1034 } else {
1035 ntfs_log_error("Failed to update "
1036 "standard informations\n");
1037 errno = EIO;
1038 res = -1;
1039 }
1040 } else
1041 res = -1;
1042 }
1043#endif
1044
1045 /* mark node as dirty */
1046 NInoSetDirty(ni);
1047 return (res);
1048}
1049
1050/*
1051 * Upgrade the security descriptor of a file
1052 * This is intended to allow graceful upgrades for files which
1053 * were created in previous versions, with a security attributes
1054 * and no security id.
1055 *
1056 * It will allocate a security id and replace the individual
1057 * security attribute by a reference to the global one
1058 *
1059 * Special files are not upgraded (currently / and files in
1060 * directories /$*)
1061 *
1062 * Though most code is similar to update_secur_desc() it has
1063 * been kept apart to facilitate the further processing of
1064 * special cases or even to remove it if found dangerous.
1065 *
1066 * returns 0 if success,
1067 * 1 if not upgradable. This is not an error.
1068 * -1 if there is a problem
1069 */
1070
1071static int upgrade_secur_desc(ntfs_volume *vol,
1072 const char *attr, ntfs_inode *ni)
1073{
1074 int attrsz;
1075 int res;
1076 le32 securid;
1077 ntfs_attr *na;
1078
1079 /*
1080 * upgrade requires NTFS format v3.x
1081 * also refuse upgrading for special files
1082 * whose number is less than FILE_first_user
1083 */
1084
1085 if ((vol->major_ver >= 3)
1086 && (ni->mft_no >= FILE_first_user)) {
1087 attrsz = ntfs_attr_size(attr);
1088 securid = setsecurityattr(vol,
1089 (const SECURITY_DESCRIPTOR_RELATIVE*)attr,
1090 (s64)attrsz);
1091 if (securid) {
1092 na = ntfs_attr_open(ni, AT_STANDARD_INFORMATION,
1093 AT_UNNAMED, 0);
1094 if (na) {
1095 /* expand standard information attribute to v3.x */
1096 res = ntfs_attr_truncate(na,
1097 (s64)sizeof(STANDARD_INFORMATION));
1098 ni->owner_id = const_cpu_to_le32(0);
1099 ni->quota_charged = const_cpu_to_le64(0);
1100 ni->usn = const_cpu_to_le64(0);
1101 ntfs_attr_remove(ni, AT_SECURITY_DESCRIPTOR,
1102 AT_UNNAMED, 0);
1103 set_nino_flag(ni, v3_Extensions);
1104 ni->security_id = securid;
1105 ntfs_attr_close(na);
1106 } else {
1107 ntfs_log_error("Failed to upgrade "
1108 "standard informations\n");
1109 errno = EIO;
1110 res = -1;
1111 }
1112 } else
1113 res = -1;
1114 /* mark node as dirty */
1115 NInoSetDirty(ni);
1116 } else
1117 res = 1;
1118
1119 return (res);
1120}
1121
1122/*
1123 * Optional simplified checking of group membership
1124 *
1125 * This only takes into account the groups defined in
1126 * /etc/group at initialization time.
1127 * It does not take into account the groups dynamically set by
1128 * setgroups() nor the changes in /etc/group since initialization
1129 *
1130 * This optional method could be useful if standard checking
1131 * leads to a performance concern.
1132 *
1133 * Should not be called for user root, however the group may be root
1134 *
1135 */
1136
1137static BOOL staticgroupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1138{
1139 BOOL ingroup;
1140 int grcnt;
1141 gid_t *groups;
1142 struct MAPPING *user;
1143
1144 ingroup = FALSE;
1145 if (uid) {
1146 user = scx->mapping[MAPUSERS];
1147 while (user && ((uid_t)user->xid != uid))
1148 user = user->next;
1149 if (user) {
1150 groups = user->groups;
1151 grcnt = user->grcnt;
1152 while ((--grcnt >= 0) && (groups[grcnt] != gid)) { }
1153 ingroup = (grcnt >= 0);
1154 }
1155 }
1156 return (ingroup);
1157}
1158
1159#if defined(__sun) && defined (__SVR4)
1160
1161/*
1162 * Check whether current thread owner is member of file group
1163 * Solaris/OpenIndiana version
1164 * Should not be called for user root, however the group may be root
1165 *
1166 * The group list is available in "/proc/$PID/cred"
1167 *
1168 */
1169
1170static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1171{
1172 typedef struct prcred {
1173 uid_t pr_euid; /* effective user id */
1174 uid_t pr_ruid; /* real user id */
1175 uid_t pr_suid; /* saved user id (from exec) */
1176 gid_t pr_egid; /* effective group id */
1177 gid_t pr_rgid; /* real group id */
1178 gid_t pr_sgid; /* saved group id (from exec) */
1179 int pr_ngroups; /* number of supplementary groups */
1180 gid_t pr_groups[1]; /* array of supplementary groups */
1181 } prcred_t;
1182 enum { readset = 16 };
1183
1184 prcred_t basecreds;
1185 gid_t groups[readset];
1186 char filename[64];
1187 int fd;
1188 int k;
1189 int cnt;
1190 gid_t *p;
1191 BOOL ismember;
1192 int got;
1193 pid_t tid;
1194
1195 if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
1196 ismember = staticgroupmember(scx, uid, gid);
1197 else {
1198 ismember = FALSE; /* default return */
1199 tid = scx->tid;
1200 sprintf(filename,"/proc/%u/cred",tid);
1201 fd = open(filename,O_RDONLY);
1202 if (fd >= 0) {
1203 got = read(fd, &basecreds, sizeof(prcred_t));
1204 if (got == sizeof(prcred_t)) {
1205 if (basecreds.pr_egid == gid)
1206 ismember = TRUE;
1207 p = basecreds.pr_groups;
1208 cnt = 1;
1209 k = 0;
1210 while (!ismember
1211 && (k < basecreds.pr_ngroups)
1212 && (cnt > 0)
1213 && (*p != gid)) {
1214 k++;
1215 cnt--;
1216 p++;
1217 if (cnt <= 0) {
1218 got = read(fd, groups,
1219 readset*sizeof(gid_t));
1220 cnt = got/sizeof(gid_t);
1221 p = groups;
1222 }
1223 }
1224 if ((cnt > 0)
1225 && (k < basecreds.pr_ngroups))
1226 ismember = TRUE;
1227 }
1228 close(fd);
1229 }
1230 }
1231 return (ismember);
1232}
1233
1234#else /* defined(__sun) && defined (__SVR4) */
1235
1236/*
1237 * Check whether current thread owner is member of file group
1238 * Linux version
1239 * Should not be called for user root, however the group may be root
1240 *
1241 * As indicated by Miklos Szeredi :
1242 *
1243 * The group list is available in
1244 *
1245 * /proc/$PID/task/$TID/status
1246 *
1247 * and fuse supplies TID in get_fuse_context()->pid. The only problem is
1248 * finding out PID, for which I have no good solution, except to iterate
1249 * through all processes. This is rather slow, but may be speeded up
1250 * with caching and heuristics (for single threaded programs PID = TID).
1251 *
1252 * The following implementation gets the group list from
1253 * /proc/$TID/task/$TID/status which apparently exists and
1254 * contains the same data.
1255 */
1256
1257static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid)
1258{
1259 static char key[] = "\nGroups:";
1260 char buf[BUFSZ+1];
1261 char filename[64];
1262 enum { INKEY, INSEP, INNUM, INEND } state;
1263 int fd;
1264 char c;
1265 int matched;
1266 BOOL ismember;
1267 int got;
1268 char *p;
1269 gid_t grp;
1270 pid_t tid;
1271
1272 if (scx->vol->secure_flags & (1 << SECURITY_STATICGRPS))
1273 ismember = staticgroupmember(scx, uid, gid);
1274 else {
1275 ismember = FALSE; /* default return */
1276 tid = scx->tid;
1277 sprintf(filename,"/proc/%u/task/%u/status",tid,tid);
1278 fd = open(filename,O_RDONLY);
1279 if (fd >= 0) {
1280 got = read(fd, buf, BUFSZ);
1281 buf[got] = 0;
1282 state = INKEY;
1283 matched = 0;
1284 p = buf;
1285 grp = 0;
1286 /*
1287 * A simple automaton to process lines like
1288 * Groups: 14 500 513
1289 */
1290 do {
1291 c = *p++;
1292 if (!c) {
1293 /* refill buffer */
1294 got = read(fd, buf, BUFSZ);
1295 buf[got] = 0;
1296 p = buf;
1297 c = *p++; /* 0 at end of file */
1298 }
1299 switch (state) {
1300 case INKEY :
1301 if (key[matched] == c) {
1302 if (!key[++matched])
1303 state = INSEP;
1304 } else
1305 if (key[0] == c)
1306 matched = 1;
1307 else
1308 matched = 0;
1309 break;
1310 case INSEP :
1311 if ((c >= '0') && (c <= '9')) {
1312 grp = c - '0';
1313 state = INNUM;
1314 } else
1315 if ((c != ' ') && (c != '\t'))
1316 state = INEND;
1317 break;
1318 case INNUM :
1319 if ((c >= '0') && (c <= '9'))
1320 grp = grp*10 + c - '0';
1321 else {
1322 ismember = (grp == gid);
1323 if ((c != ' ') && (c != '\t'))
1324 state = INEND;
1325 else
1326 state = INSEP;
1327 }
1328 default :
1329 break;
1330 }
1331 } while (!ismember && c && (state != INEND));
1332 close(fd);
1333 if (!c)
1334 ntfs_log_error("No group record found in %s\n",filename);
1335 } else
1336 ntfs_log_error("Could not open %s\n",filename);
1337 }
1338 return (ismember);
1339}
1340
1341#endif /* defined(__sun) && defined (__SVR4) */
1342
Steve Kondik79165c32015-11-09 19:43:00 -08001343#if POSIXACLS
1344
1345/*
1346 * Extract the basic permissions from a Posix ACL
1347 *
1348 * This is only to be used when Posix ACLs are compiled in,
1349 * but not enabled in the mount options.
1350 *
1351 * it replaces the permission mask by the group permissions.
1352 * If special groups are mapped, they are also considered as world.
1353 */
1354
1355static int ntfs_basic_perms(const struct SECURITY_CONTEXT *scx,
1356 const struct POSIX_SECURITY *pxdesc)
1357{
1358 int k;
1359 int perms;
1360 const struct POSIX_ACE *pace;
1361 const struct MAPPING* group;
1362
1363 k = 0;
1364 perms = pxdesc->mode;
1365 for (k=0; k < pxdesc->acccnt; k++) {
1366 pace = &pxdesc->acl.ace[k];
1367 if (pace->tag == POSIX_ACL_GROUP_OBJ)
1368 perms = (perms & 07707)
1369 | ((pace->perms & 7) << 3);
1370 else
1371 if (pace->tag == POSIX_ACL_GROUP) {
1372 group = scx->mapping[MAPGROUPS];
1373 while (group && (group->xid != pace->id))
1374 group = group->next;
1375 if (group && group->grcnt
1376 && (*(group->groups) == (gid_t)pace->id))
1377 perms |= pace->perms & 7;
1378 }
1379 }
1380 return (perms);
1381}
1382
1383#endif /* POSIXACLS */
1384
Steve Kondik2111ad72013-07-07 12:07:44 -07001385/*
1386 * Cacheing is done two-way :
1387 * - from uid, gid and perm to securid (CACHED_SECURID)
1388 * - from a securid to uid, gid and perm (CACHED_PERMISSIONS)
1389 *
1390 * CACHED_SECURID data is kept in a most-recent-first list
1391 * which should not be too long to be efficient. Its optimal
1392 * size is depends on usage and is hard to determine.
1393 *
1394 * CACHED_PERMISSIONS data is kept in a two-level indexed array. It
1395 * is optimal at the expense of storage. Use of a most-recent-first
1396 * list would save memory and provide similar performances for
1397 * standard usage, but not for file servers with too many file
1398 * owners
1399 *
1400 * CACHED_PERMISSIONS_LEGACY is a special case for CACHED_PERMISSIONS
1401 * for legacy directories which were not allocated a security_id
1402 * it is organized in a most-recent-first list.
1403 *
1404 * In main caches, data is never invalidated, as the meaning of
1405 * a security_id only changes when user mapping is changed, which
1406 * current implies remounting. However returned entries may be
1407 * overwritten at next update, so data has to be copied elsewhere
1408 * before another cache update is made.
1409 * In legacy cache, data has to be invalidated when protection is
1410 * changed.
1411 *
1412 * Though the same data may be found in both list, they
1413 * must be kept separately : the interpretation of ACL
1414 * in both direction are approximations which could be non
1415 * reciprocal for some configuration of the user mapping data
1416 *
1417 * During the process of recompiling ntfs-3g from a tgz archive,
1418 * security processing added 7.6% to the cpu time used by ntfs-3g
1419 * and 30% if the cache is disabled.
1420 */
1421
1422static struct PERMISSIONS_CACHE *create_caches(struct SECURITY_CONTEXT *scx,
1423 u32 securindex)
1424{
1425 struct PERMISSIONS_CACHE *cache;
1426 unsigned int index1;
1427 unsigned int i;
1428
1429 cache = (struct PERMISSIONS_CACHE*)NULL;
1430 /* create the first permissions blocks */
1431 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1432 cache = (struct PERMISSIONS_CACHE*)
1433 ntfs_malloc(sizeof(struct PERMISSIONS_CACHE)
1434 + index1*sizeof(struct CACHED_PERMISSIONS*));
1435 if (cache) {
1436 cache->head.last = index1;
1437 cache->head.p_reads = 0;
1438 cache->head.p_hits = 0;
1439 cache->head.p_writes = 0;
1440 *scx->pseccache = cache;
1441 for (i=0; i<=index1; i++)
1442 cache->cachetable[i]
1443 = (struct CACHED_PERMISSIONS*)NULL;
1444 }
1445 return (cache);
1446}
1447
1448/*
1449 * Free memory used by caches
1450 * The only purpose is to facilitate the detection of memory leaks
1451 */
1452
1453static void free_caches(struct SECURITY_CONTEXT *scx)
1454{
1455 unsigned int index1;
1456 struct PERMISSIONS_CACHE *pseccache;
1457
1458 pseccache = *scx->pseccache;
1459 if (pseccache) {
1460 for (index1=0; index1<=pseccache->head.last; index1++)
1461 if (pseccache->cachetable[index1]) {
1462#if POSIXACLS
1463 struct CACHED_PERMISSIONS *cacheentry;
1464 unsigned int index2;
1465
1466 for (index2=0; index2<(1<< CACHE_PERMISSIONS_BITS); index2++) {
1467 cacheentry = &pseccache->cachetable[index1][index2];
1468 if (cacheentry->valid
1469 && cacheentry->pxdesc)
1470 free(cacheentry->pxdesc);
1471 }
1472#endif
1473 free(pseccache->cachetable[index1]);
1474 }
1475 free(pseccache);
1476 }
1477}
1478
1479static int compare(const struct CACHED_SECURID *cached,
1480 const struct CACHED_SECURID *item)
1481{
1482#if POSIXACLS
1483 size_t csize;
1484 size_t isize;
1485
1486 /* only compare data and sizes */
1487 csize = (cached->variable ?
1488 sizeof(struct POSIX_ACL)
1489 + (((struct POSIX_SECURITY*)cached->variable)->acccnt
1490 + ((struct POSIX_SECURITY*)cached->variable)->defcnt)
1491 *sizeof(struct POSIX_ACE) :
1492 0);
1493 isize = (item->variable ?
1494 sizeof(struct POSIX_ACL)
1495 + (((struct POSIX_SECURITY*)item->variable)->acccnt
1496 + ((struct POSIX_SECURITY*)item->variable)->defcnt)
1497 *sizeof(struct POSIX_ACE) :
1498 0);
1499 return ((cached->uid != item->uid)
1500 || (cached->gid != item->gid)
1501 || (cached->dmode != item->dmode)
1502 || (csize != isize)
1503 || (csize
1504 && isize
1505 && memcmp(&((struct POSIX_SECURITY*)cached->variable)->acl,
1506 &((struct POSIX_SECURITY*)item->variable)->acl, csize)));
1507#else
1508 return ((cached->uid != item->uid)
1509 || (cached->gid != item->gid)
1510 || (cached->dmode != item->dmode));
1511#endif
1512}
1513
1514static int leg_compare(const struct CACHED_PERMISSIONS_LEGACY *cached,
1515 const struct CACHED_PERMISSIONS_LEGACY *item)
1516{
1517 return (cached->mft_no != item->mft_no);
1518}
1519
1520/*
1521 * Resize permission cache table
1522 * do not call unless resizing is needed
1523 *
1524 * If allocation fails, the cache size is not updated
1525 * Lack of memory is not considered as an error, the cache is left
1526 * consistent and errno is not set.
1527 */
1528
1529static void resize_cache(struct SECURITY_CONTEXT *scx,
1530 u32 securindex)
1531{
1532 struct PERMISSIONS_CACHE *oldcache;
1533 struct PERMISSIONS_CACHE *newcache;
1534 int newcnt;
1535 int oldcnt;
1536 unsigned int index1;
1537 unsigned int i;
1538
1539 oldcache = *scx->pseccache;
1540 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1541 newcnt = index1 + 1;
1542 if (newcnt <= ((CACHE_PERMISSIONS_SIZE
1543 + (1 << CACHE_PERMISSIONS_BITS)
1544 - 1) >> CACHE_PERMISSIONS_BITS)) {
1545 /* expand cache beyond current end, do not use realloc() */
1546 /* to avoid losing data when there is no more memory */
1547 oldcnt = oldcache->head.last + 1;
1548 newcache = (struct PERMISSIONS_CACHE*)
1549 ntfs_malloc(
1550 sizeof(struct PERMISSIONS_CACHE)
1551 + (newcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1552 if (newcache) {
1553 memcpy(newcache,oldcache,
1554 sizeof(struct PERMISSIONS_CACHE)
1555 + (oldcnt - 1)*sizeof(struct CACHED_PERMISSIONS*));
1556 free(oldcache);
1557 /* mark new entries as not valid */
1558 for (i=newcache->head.last+1; i<=index1; i++)
1559 newcache->cachetable[i]
1560 = (struct CACHED_PERMISSIONS*)NULL;
1561 newcache->head.last = index1;
1562 *scx->pseccache = newcache;
1563 }
1564 }
1565}
1566
1567/*
1568 * Enter uid, gid and mode into cache, if possible
1569 *
1570 * returns the updated or created cache entry,
1571 * or NULL if not possible (typically if there is no
1572 * security id associated)
1573 */
1574
1575#if POSIXACLS
1576static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1577 ntfs_inode *ni, uid_t uid, gid_t gid,
1578 struct POSIX_SECURITY *pxdesc)
1579#else
1580static struct CACHED_PERMISSIONS *enter_cache(struct SECURITY_CONTEXT *scx,
1581 ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode)
1582#endif
1583{
1584 struct CACHED_PERMISSIONS *cacheentry;
1585 struct CACHED_PERMISSIONS *cacheblock;
1586 struct PERMISSIONS_CACHE *pcache;
1587 u32 securindex;
1588#if POSIXACLS
1589 int pxsize;
1590 struct POSIX_SECURITY *pxcached;
1591#endif
1592 unsigned int index1;
1593 unsigned int index2;
1594 int i;
1595
1596 /* cacheing is only possible if a security_id has been defined */
1597 if (test_nino_flag(ni, v3_Extensions)
1598 && ni->security_id) {
1599 /*
1600 * Immediately test the most frequent situation
1601 * where the entry exists
1602 */
1603 securindex = le32_to_cpu(ni->security_id);
1604 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1605 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1606 pcache = *scx->pseccache;
1607 if (pcache
1608 && (pcache->head.last >= index1)
1609 && pcache->cachetable[index1]) {
1610 cacheentry = &pcache->cachetable[index1][index2];
1611 cacheentry->uid = uid;
1612 cacheentry->gid = gid;
1613#if POSIXACLS
1614 if (cacheentry->valid && cacheentry->pxdesc)
1615 free(cacheentry->pxdesc);
1616 if (pxdesc) {
1617 pxsize = sizeof(struct POSIX_SECURITY)
1618 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1619 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1620 if (pxcached) {
1621 memcpy(pxcached, pxdesc, pxsize);
1622 cacheentry->pxdesc = pxcached;
1623 } else {
1624 cacheentry->valid = 0;
1625 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1626 }
1627 cacheentry->mode = pxdesc->mode & 07777;
1628 } else
1629 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1630#else
1631 cacheentry->mode = mode & 07777;
1632#endif
1633 cacheentry->inh_fileid = const_cpu_to_le32(0);
1634 cacheentry->inh_dirid = const_cpu_to_le32(0);
1635 cacheentry->valid = 1;
1636 pcache->head.p_writes++;
1637 } else {
1638 if (!pcache) {
1639 /* create the first cache block */
1640 pcache = create_caches(scx, securindex);
1641 } else {
1642 if (index1 > pcache->head.last) {
1643 resize_cache(scx, securindex);
1644 pcache = *scx->pseccache;
1645 }
1646 }
1647 /* allocate block, if cache table was allocated */
1648 if (pcache && (index1 <= pcache->head.last)) {
1649 cacheblock = (struct CACHED_PERMISSIONS*)
1650 malloc(sizeof(struct CACHED_PERMISSIONS)
1651 << CACHE_PERMISSIONS_BITS);
1652 pcache->cachetable[index1] = cacheblock;
1653 for (i=0; i<(1 << CACHE_PERMISSIONS_BITS); i++)
1654 cacheblock[i].valid = 0;
1655 cacheentry = &cacheblock[index2];
1656 if (cacheentry) {
1657 cacheentry->uid = uid;
1658 cacheentry->gid = gid;
1659#if POSIXACLS
1660 if (pxdesc) {
1661 pxsize = sizeof(struct POSIX_SECURITY)
1662 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1663 pxcached = (struct POSIX_SECURITY*)malloc(pxsize);
1664 if (pxcached) {
1665 memcpy(pxcached, pxdesc, pxsize);
1666 cacheentry->pxdesc = pxcached;
1667 } else {
1668 cacheentry->valid = 0;
1669 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1670 }
1671 cacheentry->mode = pxdesc->mode & 07777;
1672 } else
1673 cacheentry->pxdesc = (struct POSIX_SECURITY*)NULL;
1674#else
1675 cacheentry->mode = mode & 07777;
1676#endif
1677 cacheentry->inh_fileid = const_cpu_to_le32(0);
1678 cacheentry->inh_dirid = const_cpu_to_le32(0);
1679 cacheentry->valid = 1;
1680 pcache->head.p_writes++;
1681 }
1682 } else
1683 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1684 }
1685 } else {
1686 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1687#if CACHE_LEGACY_SIZE
1688 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1689 struct CACHED_PERMISSIONS_LEGACY wanted;
1690 struct CACHED_PERMISSIONS_LEGACY *legacy;
1691
1692 wanted.perm.uid = uid;
1693 wanted.perm.gid = gid;
1694#if POSIXACLS
1695 wanted.perm.mode = pxdesc->mode & 07777;
1696 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1697 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1698 wanted.mft_no = ni->mft_no;
1699 wanted.variable = (void*)pxdesc;
1700 wanted.varsize = sizeof(struct POSIX_SECURITY)
1701 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
1702#else
1703 wanted.perm.mode = mode & 07777;
1704 wanted.perm.inh_fileid = const_cpu_to_le32(0);
1705 wanted.perm.inh_dirid = const_cpu_to_le32(0);
1706 wanted.mft_no = ni->mft_no;
1707 wanted.variable = (void*)NULL;
1708 wanted.varsize = 0;
1709#endif
1710 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_enter_cache(
1711 scx->vol->legacy_cache, GENERIC(&wanted),
1712 (cache_compare)leg_compare);
1713 if (legacy) {
1714 cacheentry = &legacy->perm;
1715#if POSIXACLS
1716 /*
1717 * give direct access to the cached pxdesc
1718 * in the permissions structure
1719 */
1720 cacheentry->pxdesc = legacy->variable;
1721#endif
1722 }
1723 }
1724#endif
1725 }
1726 return (cacheentry);
1727}
1728
1729/*
1730 * Fetch owner, group and permission of a file, if cached
1731 *
1732 * Beware : do not use the returned entry after a cache update :
1733 * the cache may be relocated making the returned entry meaningless
1734 *
1735 * returns the cache entry, or NULL if not available
1736 */
1737
1738static struct CACHED_PERMISSIONS *fetch_cache(struct SECURITY_CONTEXT *scx,
1739 ntfs_inode *ni)
1740{
1741 struct CACHED_PERMISSIONS *cacheentry;
1742 struct PERMISSIONS_CACHE *pcache;
1743 u32 securindex;
1744 unsigned int index1;
1745 unsigned int index2;
1746
1747 /* cacheing is only possible if a security_id has been defined */
1748 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1749 if (test_nino_flag(ni, v3_Extensions)
1750 && (ni->security_id)) {
1751 securindex = le32_to_cpu(ni->security_id);
1752 index1 = securindex >> CACHE_PERMISSIONS_BITS;
1753 index2 = securindex & ((1 << CACHE_PERMISSIONS_BITS) - 1);
1754 pcache = *scx->pseccache;
1755 if (pcache
1756 && (pcache->head.last >= index1)
1757 && pcache->cachetable[index1]) {
1758 cacheentry = &pcache->cachetable[index1][index2];
1759 /* reject if entry is not valid */
1760 if (!cacheentry->valid)
1761 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1762 else
1763 pcache->head.p_hits++;
1764 if (pcache)
1765 pcache->head.p_reads++;
1766 }
1767 }
1768#if CACHE_LEGACY_SIZE
1769 else {
1770 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1771 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
1772 struct CACHED_PERMISSIONS_LEGACY wanted;
1773 struct CACHED_PERMISSIONS_LEGACY *legacy;
1774
1775 wanted.mft_no = ni->mft_no;
1776 wanted.variable = (void*)NULL;
1777 wanted.varsize = 0;
1778 legacy = (struct CACHED_PERMISSIONS_LEGACY*)ntfs_fetch_cache(
1779 scx->vol->legacy_cache, GENERIC(&wanted),
1780 (cache_compare)leg_compare);
1781 if (legacy) cacheentry = &legacy->perm;
1782 }
1783 }
1784#endif
1785#if POSIXACLS
1786 if (cacheentry && !cacheentry->pxdesc) {
1787 ntfs_log_error("No Posix descriptor in cache\n");
1788 cacheentry = (struct CACHED_PERMISSIONS*)NULL;
1789 }
1790#endif
1791 return (cacheentry);
1792}
1793
1794/*
1795 * Retrieve a security attribute from $Secure
1796 */
1797
1798static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id)
1799{
1800 struct SII *psii;
1801 union {
1802 struct {
1803 le32 dataoffsl;
1804 le32 dataoffsh;
1805 } parts;
1806 le64 all;
1807 } realign;
1808 int found;
1809 size_t size;
1810 size_t rdsize;
1811 s64 offs;
1812 ntfs_inode *ni;
1813 ntfs_index_context *xsii;
1814 char *securattr;
1815
1816 securattr = (char*)NULL;
1817 ni = vol->secure_ni;
1818 xsii = vol->secure_xsii;
1819 if (ni && xsii) {
1820 ntfs_index_ctx_reinit(xsii);
1821 found =
1822 !ntfs_index_lookup((char*)&id,
1823 sizeof(SII_INDEX_KEY), xsii);
1824 if (found) {
1825 psii = (struct SII*)xsii->entry;
1826 size =
1827 (size_t) le32_to_cpu(psii->datasize)
1828 - sizeof(SECURITY_DESCRIPTOR_HEADER);
1829 /* work around bad alignment problem */
1830 realign.parts.dataoffsh = psii->dataoffsh;
1831 realign.parts.dataoffsl = psii->dataoffsl;
1832 offs = le64_to_cpu(realign.all)
1833 + sizeof(SECURITY_DESCRIPTOR_HEADER);
1834
1835 securattr = (char*)ntfs_malloc(size);
1836 if (securattr) {
1837 rdsize = ntfs_attr_data_read(
1838 ni, STREAM_SDS, 4,
1839 securattr, size, offs);
1840 if ((rdsize != size)
1841 || !ntfs_valid_descr(securattr,
1842 rdsize)) {
1843 /* error to be logged by caller */
1844 free(securattr);
1845 securattr = (char*)NULL;
1846 }
1847 }
1848 } else
1849 if (errno != ENOENT)
1850 ntfs_log_perror("Inconsistency in index $SII");
1851 }
1852 if (!securattr) {
1853 ntfs_log_error("Failed to retrieve a security descriptor\n");
1854 errno = EIO;
1855 }
1856 return (securattr);
1857}
1858
1859/*
1860 * Get the security descriptor associated to a file
1861 *
1862 * Either :
1863 * - read the security descriptor attribute (v1.x format)
1864 * - or find the descriptor in $Secure:$SDS (v3.x format)
1865 *
1866 * in both case, sanity checks are done on the attribute and
1867 * the descriptor can be assumed safe
1868 *
1869 * The returned descriptor is dynamically allocated and has to be freed
1870 */
1871
1872static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni)
1873{
1874 SII_INDEX_KEY securid;
1875 char *securattr;
1876 s64 readallsz;
1877
1878 /*
1879 * Warning : in some situations, after fixing by chkdsk,
1880 * v3_Extensions are marked present (long standard informations)
1881 * with a default security descriptor inserted in an
1882 * attribute
1883 */
1884 if (test_nino_flag(ni, v3_Extensions)
Steve Kondik79165c32015-11-09 19:43:00 -08001885 && vol->secure_ni && ni->security_id) {
Steve Kondik2111ad72013-07-07 12:07:44 -07001886 /* get v3.x descriptor in $Secure */
1887 securid.security_id = ni->security_id;
1888 securattr = retrievesecurityattr(vol,securid);
1889 if (!securattr)
1890 ntfs_log_error("Bad security descriptor for 0x%lx\n",
1891 (long)le32_to_cpu(ni->security_id));
1892 } else {
1893 /* get v1.x security attribute */
1894 readallsz = 0;
1895 securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR,
1896 AT_UNNAMED, 0, &readallsz);
1897 if (securattr && !ntfs_valid_descr(securattr, readallsz)) {
1898 ntfs_log_error("Bad security descriptor for inode %lld\n",
1899 (long long)ni->mft_no);
1900 free(securattr);
1901 securattr = (char*)NULL;
1902 }
1903 }
1904 if (!securattr) {
1905 /*
1906 * in some situations, there is no security
1907 * descriptor, and chkdsk does not detect or fix
1908 * anything. This could be a normal situation.
1909 * When this happens, simulate a descriptor with
1910 * minimum rights, so that a real descriptor can
1911 * be created by chown or chmod
1912 */
1913 ntfs_log_error("No security descriptor found for inode %lld\n",
1914 (long long)ni->mft_no);
1915 securattr = ntfs_build_descr(0, 0, adminsid, adminsid);
1916 }
1917 return (securattr);
1918}
1919
1920#if POSIXACLS
1921
1922/*
1923 * Determine which access types to a file are allowed
1924 * according to the relation of current process to the file
1925 *
Steve Kondik79165c32015-11-09 19:43:00 -08001926 * When Posix ACLs are compiled in but not enabled in the mount
1927 * options POSIX_ACL_USER, POSIX_ACL_GROUP and POSIX_ACL_MASK
1928 * are ignored.
Steve Kondik2111ad72013-07-07 12:07:44 -07001929 */
1930
1931static int access_check_posix(struct SECURITY_CONTEXT *scx,
1932 struct POSIX_SECURITY *pxdesc, mode_t request,
1933 uid_t uid, gid_t gid)
1934{
1935 struct POSIX_ACE *pxace;
1936 int userperms;
1937 int groupperms;
1938 int mask;
1939 BOOL somegroup;
1940 BOOL needgroups;
Steve Kondik79165c32015-11-09 19:43:00 -08001941 BOOL noacl;
Steve Kondik2111ad72013-07-07 12:07:44 -07001942 mode_t perms;
1943 int i;
1944
Steve Kondik79165c32015-11-09 19:43:00 -08001945 noacl = !(scx->vol->secure_flags & (1 << SECURITY_ACL));
1946 if (noacl)
1947 perms = ntfs_basic_perms(scx, pxdesc);
1948 else
1949 perms = pxdesc->mode;
Steve Kondik2111ad72013-07-07 12:07:44 -07001950 /* owner and root access */
1951 if (!scx->uid || (uid == scx->uid)) {
1952 if (!scx->uid) {
1953 /* root access if owner or other execution */
1954 if (perms & 0101)
1955 perms |= 01777;
1956 else {
1957 /* root access if some group execution */
1958 groupperms = 0;
1959 mask = 7;
1960 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1961 pxace = &pxdesc->acl.ace[i];
1962 switch (pxace->tag) {
1963 case POSIX_ACL_USER_OBJ :
1964 case POSIX_ACL_GROUP_OBJ :
Steve Kondik2111ad72013-07-07 12:07:44 -07001965 groupperms |= pxace->perms;
1966 break;
Steve Kondik79165c32015-11-09 19:43:00 -08001967 case POSIX_ACL_GROUP :
1968 if (!noacl)
1969 groupperms
1970 |= pxace->perms;
1971 break;
Steve Kondik2111ad72013-07-07 12:07:44 -07001972 case POSIX_ACL_MASK :
Steve Kondik79165c32015-11-09 19:43:00 -08001973 if (!noacl)
1974 mask = pxace->perms & 7;
Steve Kondik2111ad72013-07-07 12:07:44 -07001975 break;
1976 default :
1977 break;
1978 }
1979 }
1980 perms = (groupperms & mask & 1) | 6;
1981 }
1982 } else
1983 perms &= 07700;
1984 } else {
1985 /*
1986 * analyze designated users, get mask
1987 * and identify whether we need to check
1988 * the group memberships. The groups are
1989 * not needed when all groups have the
1990 * same permissions as other for the
1991 * requested modes.
1992 */
1993 userperms = -1;
1994 groupperms = -1;
1995 needgroups = FALSE;
1996 mask = 7;
1997 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
1998 pxace = &pxdesc->acl.ace[i];
1999 switch (pxace->tag) {
2000 case POSIX_ACL_USER :
Steve Kondik79165c32015-11-09 19:43:00 -08002001 if (!noacl
2002 && ((uid_t)pxace->id == scx->uid))
Steve Kondik2111ad72013-07-07 12:07:44 -07002003 userperms = pxace->perms;
2004 break;
2005 case POSIX_ACL_MASK :
Steve Kondik79165c32015-11-09 19:43:00 -08002006 if (!noacl)
2007 mask = pxace->perms & 7;
Steve Kondik2111ad72013-07-07 12:07:44 -07002008 break;
2009 case POSIX_ACL_GROUP_OBJ :
Steve Kondik2111ad72013-07-07 12:07:44 -07002010 if (((pxace->perms & mask) ^ perms)
2011 & (request >> 6) & 7)
2012 needgroups = TRUE;
2013 break;
Steve Kondik79165c32015-11-09 19:43:00 -08002014 case POSIX_ACL_GROUP :
2015 if (!noacl
2016 && (((pxace->perms & mask) ^ perms)
2017 & (request >> 6) & 7))
2018 needgroups = TRUE;
2019 break;
Steve Kondik2111ad72013-07-07 12:07:44 -07002020 default :
2021 break;
2022 }
2023 }
2024 /* designated users */
2025 if (userperms >= 0)
2026 perms = (perms & 07000) + (userperms & mask);
2027 else if (!needgroups)
2028 perms &= 07007;
2029 else {
2030 /* owning group */
2031 if (!(~(perms >> 3) & request & mask)
2032 && ((gid == scx->gid)
2033 || groupmember(scx, scx->uid, gid)))
2034 perms &= 07070;
Steve Kondik79165c32015-11-09 19:43:00 -08002035 else if (!noacl) {
Steve Kondik2111ad72013-07-07 12:07:44 -07002036 /* other groups */
2037 groupperms = -1;
2038 somegroup = FALSE;
2039 for (i=pxdesc->acccnt-1; i>=0 ; i--) {
2040 pxace = &pxdesc->acl.ace[i];
2041 if ((pxace->tag == POSIX_ACL_GROUP)
Steve Kondik79165c32015-11-09 19:43:00 -08002042 && groupmember(scx, scx->uid, pxace->id)) {
Steve Kondik2111ad72013-07-07 12:07:44 -07002043 if (!(~pxace->perms & request & mask))
2044 groupperms = pxace->perms;
2045 somegroup = TRUE;
2046 }
2047 }
2048 if (groupperms >= 0)
2049 perms = (perms & 07000) + (groupperms & mask);
2050 else
2051 if (somegroup)
2052 perms = 0;
2053 else
2054 perms &= 07007;
Steve Kondik79165c32015-11-09 19:43:00 -08002055 } else
2056 perms &= 07007;
Steve Kondik2111ad72013-07-07 12:07:44 -07002057 }
2058 }
2059 return (perms);
2060}
2061
2062/*
2063 * Get permissions to access a file
2064 * Takes into account the relation of user to file (owner, group, ...)
2065 * Do no use as mode of the file
2066 * Do no call if default_permissions is set
2067 *
2068 * returns -1 if there is a problem
2069 */
2070
2071static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
2072 ntfs_inode * ni, mode_t request)
2073{
2074 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2075 const struct CACHED_PERMISSIONS *cached;
2076 char *securattr;
2077 const SID *usid; /* owner of file/directory */
2078 const SID *gsid; /* group of file/directory */
2079 uid_t uid;
2080 gid_t gid;
2081 int perm;
2082 BOOL isdir;
2083 struct POSIX_SECURITY *pxdesc;
2084
2085 if (!scx->mapping[MAPUSERS])
2086 perm = 07777;
2087 else {
2088 /* check whether available in cache */
2089 cached = fetch_cache(scx,ni);
2090 if (cached) {
2091 uid = cached->uid;
2092 gid = cached->gid;
2093 perm = access_check_posix(scx,cached->pxdesc,request,uid,gid);
2094 } else {
2095 perm = 0; /* default to no permission */
2096 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2097 != const_cpu_to_le16(0);
2098 securattr = getsecurityattr(scx->vol, ni);
2099 if (securattr) {
2100 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2101 securattr;
2102 gsid = (const SID*)&
2103 securattr[le32_to_cpu(phead->group)];
2104 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2105#if OWNERFROMACL
2106 usid = ntfs_acl_owner(securattr);
2107 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2108 usid, gsid, isdir);
2109 if (pxdesc)
2110 perm = pxdesc->mode & 07777;
2111 else
2112 perm = -1;
2113 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2114#else
2115 usid = (const SID*)&
2116 securattr[le32_to_cpu(phead->owner)];
2117 pxdesc = ntfs_build_permissions_posix(scx,securattr,
2118 usid, gsid, isdir);
2119 if (pxdesc)
2120 perm = pxdesc->mode & 07777;
2121 else
2122 perm = -1;
2123 if (!perm && ntfs_same_sid(usid, adminsid)) {
2124 uid = find_tenant(scx, securattr);
2125 if (uid)
2126 perm = 0700;
2127 } else
2128 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2129#endif
2130 /*
2131 * Create a security id if there were none
2132 * and upgrade option is selected
2133 */
2134 if (!test_nino_flag(ni, v3_Extensions)
2135 && (perm >= 0)
2136 && (scx->vol->secure_flags
2137 & (1 << SECURITY_ADDSECURIDS))) {
2138 upgrade_secur_desc(scx->vol,
2139 securattr, ni);
2140 /*
2141 * fetch owner and group for cacheing
2142 * if there is a securid
2143 */
2144 }
2145 if (test_nino_flag(ni, v3_Extensions)
2146 && (perm >= 0)) {
2147 enter_cache(scx, ni, uid,
2148 gid, pxdesc);
2149 }
2150 if (pxdesc) {
2151 perm = access_check_posix(scx,pxdesc,request,uid,gid);
2152 free(pxdesc);
2153 }
2154 free(securattr);
2155 } else {
2156 perm = -1;
2157 uid = gid = 0;
2158 }
2159 }
2160 }
2161 return (perm);
2162}
2163
2164/*
2165 * Get a Posix ACL
2166 *
2167 * returns size or -errno if there is a problem
2168 * if size was too small, no copy is done and errno is not set,
2169 * the caller is expected to issue a new call
2170 */
2171
2172int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2173 const char *name, char *value, size_t size)
2174{
2175 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2176 struct POSIX_SECURITY *pxdesc;
2177 const struct CACHED_PERMISSIONS *cached;
2178 char *securattr;
2179 const SID *usid; /* owner of file/directory */
2180 const SID *gsid; /* group of file/directory */
2181 uid_t uid;
2182 gid_t gid;
2183 BOOL isdir;
2184 size_t outsize;
2185
2186 outsize = 0; /* default to error */
2187 if (!scx->mapping[MAPUSERS])
2188 errno = ENOTSUP;
2189 else {
2190 /* check whether available in cache */
2191 cached = fetch_cache(scx,ni);
2192 if (cached)
2193 pxdesc = cached->pxdesc;
2194 else {
2195 securattr = getsecurityattr(scx->vol, ni);
2196 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2197 != const_cpu_to_le16(0);
2198 if (securattr) {
2199 phead =
2200 (const SECURITY_DESCRIPTOR_RELATIVE*)
2201 securattr;
2202 gsid = (const SID*)&
2203 securattr[le32_to_cpu(phead->group)];
2204#if OWNERFROMACL
2205 usid = ntfs_acl_owner(securattr);
2206#else
2207 usid = (const SID*)&
2208 securattr[le32_to_cpu(phead->owner)];
2209#endif
2210 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2211 usid, gsid, isdir);
2212
2213 /*
2214 * fetch owner and group for cacheing
2215 */
2216 if (pxdesc) {
2217 /*
2218 * Create a security id if there were none
2219 * and upgrade option is selected
2220 */
2221 if (!test_nino_flag(ni, v3_Extensions)
2222 && (scx->vol->secure_flags
2223 & (1 << SECURITY_ADDSECURIDS))) {
2224 upgrade_secur_desc(scx->vol,
2225 securattr, ni);
2226 }
2227#if OWNERFROMACL
2228 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2229#else
2230 if (!(pxdesc->mode & 07777)
2231 && ntfs_same_sid(usid, adminsid)) {
2232 uid = find_tenant(scx,
2233 securattr);
2234 } else
2235 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2236#endif
2237 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2238 if (pxdesc->tagsset & POSIX_ACL_EXTENSIONS)
2239 enter_cache(scx, ni, uid,
2240 gid, pxdesc);
2241 }
2242 free(securattr);
2243 } else
2244 pxdesc = (struct POSIX_SECURITY*)NULL;
2245 }
2246
2247 if (pxdesc) {
2248 if (ntfs_valid_posix(pxdesc)) {
2249 if (!strcmp(name,"system.posix_acl_default")) {
2250 if (ni->mrec->flags
2251 & MFT_RECORD_IS_DIRECTORY)
2252 outsize = sizeof(struct POSIX_ACL)
2253 + pxdesc->defcnt*sizeof(struct POSIX_ACE);
2254 else {
2255 /*
2256 * getting default ACL from plain file :
2257 * return EACCES if size > 0 as
2258 * indicated in the man, but return ok
2259 * if size == 0, so that ls does not
2260 * display an error
2261 */
2262 if (size > 0) {
2263 outsize = 0;
2264 errno = EACCES;
2265 } else
2266 outsize = sizeof(struct POSIX_ACL);
2267 }
2268 if (outsize && (outsize <= size)) {
2269 memcpy(value,&pxdesc->acl,sizeof(struct POSIX_ACL));
2270 memcpy(&value[sizeof(struct POSIX_ACL)],
2271 &pxdesc->acl.ace[pxdesc->firstdef],
2272 outsize-sizeof(struct POSIX_ACL));
2273 }
2274 } else {
2275 outsize = sizeof(struct POSIX_ACL)
2276 + pxdesc->acccnt*sizeof(struct POSIX_ACE);
2277 if (outsize <= size)
2278 memcpy(value,&pxdesc->acl,outsize);
2279 }
2280 } else {
2281 outsize = 0;
2282 errno = EIO;
2283 ntfs_log_error("Invalid Posix ACL built\n");
2284 }
2285 if (!cached)
2286 free(pxdesc);
2287 } else
2288 outsize = 0;
2289 }
2290 return (outsize ? (int)outsize : -errno);
2291}
2292
2293#else /* POSIXACLS */
2294
2295
2296/*
2297 * Get permissions to access a file
2298 * Takes into account the relation of user to file (owner, group, ...)
2299 * Do no use as mode of the file
2300 *
2301 * returns -1 if there is a problem
2302 */
2303
2304static int ntfs_get_perm(struct SECURITY_CONTEXT *scx,
2305 ntfs_inode *ni, mode_t request)
2306{
2307 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2308 const struct CACHED_PERMISSIONS *cached;
2309 char *securattr;
2310 const SID *usid; /* owner of file/directory */
2311 const SID *gsid; /* group of file/directory */
2312 BOOL isdir;
2313 uid_t uid;
2314 gid_t gid;
2315 int perm;
2316
2317 if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC)))
2318 perm = 07777;
2319 else {
2320 /* check whether available in cache */
2321 cached = fetch_cache(scx,ni);
2322 if (cached) {
2323 perm = cached->mode;
2324 uid = cached->uid;
2325 gid = cached->gid;
2326 } else {
2327 perm = 0; /* default to no permission */
2328 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2329 != const_cpu_to_le16(0);
2330 securattr = getsecurityattr(scx->vol, ni);
2331 if (securattr) {
2332 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2333 securattr;
2334 gsid = (const SID*)&
2335 securattr[le32_to_cpu(phead->group)];
2336 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2337#if OWNERFROMACL
2338 usid = ntfs_acl_owner(securattr);
2339 perm = ntfs_build_permissions(securattr,
2340 usid, gsid, isdir);
2341 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2342#else
2343 usid = (const SID*)&
2344 securattr[le32_to_cpu(phead->owner)];
2345 perm = ntfs_build_permissions(securattr,
2346 usid, gsid, isdir);
2347 if (!perm && ntfs_same_sid(usid, adminsid)) {
2348 uid = find_tenant(scx, securattr);
2349 if (uid)
2350 perm = 0700;
2351 } else
2352 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2353#endif
2354 /*
2355 * Create a security id if there were none
2356 * and upgrade option is selected
2357 */
2358 if (!test_nino_flag(ni, v3_Extensions)
2359 && (perm >= 0)
2360 && (scx->vol->secure_flags
2361 & (1 << SECURITY_ADDSECURIDS))) {
2362 upgrade_secur_desc(scx->vol,
2363 securattr, ni);
2364 /*
2365 * fetch owner and group for cacheing
2366 * if there is a securid
2367 */
2368 }
2369 if (test_nino_flag(ni, v3_Extensions)
2370 && (perm >= 0)) {
2371 enter_cache(scx, ni, uid,
2372 gid, perm);
2373 }
2374 free(securattr);
2375 } else {
2376 perm = -1;
2377 uid = gid = 0;
2378 }
2379 }
2380 if (perm >= 0) {
2381 if (!scx->uid) {
2382 /* root access and execution */
2383 if (perm & 0111)
2384 perm |= 01777;
2385 else
2386 perm = 0;
2387 } else
2388 if (uid == scx->uid)
2389 perm &= 07700;
2390 else
2391 /*
2392 * avoid checking group membership
2393 * when the requested perms for group
2394 * are the same as perms for other
2395 */
2396 if ((gid == scx->gid)
2397 || ((((perm >> 3) ^ perm)
2398 & (request >> 6) & 7)
2399 && groupmember(scx, scx->uid, gid)))
2400 perm &= 07070;
2401 else
2402 perm &= 07007;
2403 }
2404 }
2405 return (perm);
2406}
2407
Steve Kondik79165c32015-11-09 19:43:00 -08002408#endif /* POSIXACLS */
Steve Kondik2111ad72013-07-07 12:07:44 -07002409
2410/*
2411 * Get an NTFS ACL
2412 *
2413 * Returns size or -errno if there is a problem
2414 * if size was too small, no copy is done and errno is not set,
2415 * the caller is expected to issue a new call
2416 */
2417
2418int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2419 char *value, size_t size)
2420{
2421 char *securattr;
2422 size_t outsize;
2423
2424 outsize = 0; /* default to no data and no error */
2425 securattr = getsecurityattr(scx->vol, ni);
2426 if (securattr) {
2427 outsize = ntfs_attr_size(securattr);
2428 if (outsize <= size) {
2429 memcpy(value,securattr,outsize);
2430 }
2431 free(securattr);
2432 }
2433 return (outsize ? (int)outsize : -errno);
2434}
2435
2436/*
2437 * Get owner, group and permissions in an stat structure
2438 * returns permissions, or -1 if there is a problem
2439 */
2440
2441int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx,
2442 ntfs_inode * ni, struct stat *stbuf)
2443{
2444 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2445 char *securattr;
2446 const SID *usid; /* owner of file/directory */
2447 const SID *gsid; /* group of file/directory */
2448 const struct CACHED_PERMISSIONS *cached;
2449 int perm;
2450 BOOL isdir;
2451#if POSIXACLS
2452 struct POSIX_SECURITY *pxdesc;
2453#endif
2454
2455 if (!scx->mapping[MAPUSERS])
2456 perm = 07777;
2457 else {
2458 /* check whether available in cache */
2459 cached = fetch_cache(scx,ni);
2460 if (cached) {
Steve Kondik79165c32015-11-09 19:43:00 -08002461#if POSIXACLS
2462 if (!(scx->vol->secure_flags & (1 << SECURITY_ACL))
2463 && cached->pxdesc)
2464 perm = ntfs_basic_perms(scx,cached->pxdesc);
2465 else
2466#endif
2467 perm = cached->mode;
Steve Kondik2111ad72013-07-07 12:07:44 -07002468 stbuf->st_uid = cached->uid;
2469 stbuf->st_gid = cached->gid;
2470 stbuf->st_mode = (stbuf->st_mode & ~07777) + perm;
2471 } else {
2472 perm = -1; /* default to error */
2473 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
2474 != const_cpu_to_le16(0);
2475 securattr = getsecurityattr(scx->vol, ni);
2476 if (securattr) {
2477 phead =
2478 (const SECURITY_DESCRIPTOR_RELATIVE*)
2479 securattr;
2480 gsid = (const SID*)&
2481 securattr[le32_to_cpu(phead->group)];
2482#if OWNERFROMACL
2483 usid = ntfs_acl_owner(securattr);
2484#else
2485 usid = (const SID*)&
2486 securattr[le32_to_cpu(phead->owner)];
2487#endif
2488#if POSIXACLS
Steve Kondik79165c32015-11-09 19:43:00 -08002489 pxdesc = ntfs_build_permissions_posix(
2490 scx->mapping, securattr,
2491 usid, gsid, isdir);
2492 if (pxdesc) {
2493 if (!(scx->vol->secure_flags
2494 & (1 << SECURITY_ACL)))
2495 perm = ntfs_basic_perms(scx,
2496 pxdesc);
2497 else
2498 perm = pxdesc->mode & 07777;
2499 } else
Steve Kondik2111ad72013-07-07 12:07:44 -07002500 perm = -1;
2501#else
2502 perm = ntfs_build_permissions(securattr,
2503 usid, gsid, isdir);
2504#endif
2505 /*
2506 * fetch owner and group for cacheing
2507 */
2508 if (perm >= 0) {
2509 /*
2510 * Create a security id if there were none
2511 * and upgrade option is selected
2512 */
2513 if (!test_nino_flag(ni, v3_Extensions)
2514 && (scx->vol->secure_flags
2515 & (1 << SECURITY_ADDSECURIDS))) {
2516 upgrade_secur_desc(scx->vol,
2517 securattr, ni);
2518 }
2519#if OWNERFROMACL
2520 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2521#else
2522 if (!perm && ntfs_same_sid(usid, adminsid)) {
2523 stbuf->st_uid =
2524 find_tenant(scx,
2525 securattr);
2526 if (stbuf->st_uid)
2527 perm = 0700;
2528 } else
2529 stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2530#endif
2531 stbuf->st_gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2532 stbuf->st_mode =
2533 (stbuf->st_mode & ~07777) + perm;
2534#if POSIXACLS
2535 enter_cache(scx, ni, stbuf->st_uid,
2536 stbuf->st_gid, pxdesc);
2537 free(pxdesc);
2538#else
2539 enter_cache(scx, ni, stbuf->st_uid,
2540 stbuf->st_gid, perm);
2541#endif
2542 }
2543 free(securattr);
2544 }
2545 }
2546 }
2547 return (perm);
2548}
2549
2550#if POSIXACLS
2551
2552/*
2553 * Get the base for a Posix inheritance and
2554 * build an inherited Posix descriptor
2555 */
2556
2557static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx,
2558 ntfs_inode *dir_ni, mode_t mode, BOOL isdir)
2559{
2560 const struct CACHED_PERMISSIONS *cached;
2561 const SECURITY_DESCRIPTOR_RELATIVE *phead;
2562 struct POSIX_SECURITY *pxdesc;
2563 struct POSIX_SECURITY *pydesc;
2564 char *securattr;
2565 const SID *usid;
2566 const SID *gsid;
2567 uid_t uid;
2568 gid_t gid;
2569
2570 pydesc = (struct POSIX_SECURITY*)NULL;
2571 /* check whether parent directory is available in cache */
2572 cached = fetch_cache(scx,dir_ni);
2573 if (cached) {
2574 uid = cached->uid;
2575 gid = cached->gid;
2576 pxdesc = cached->pxdesc;
2577 if (pxdesc) {
Steve Kondik79165c32015-11-09 19:43:00 -08002578 if (scx->vol->secure_flags & (1 << SECURITY_ACL))
2579 pydesc = ntfs_build_inherited_posix(pxdesc,
2580 mode, scx->umask, isdir);
2581 else
2582 pydesc = ntfs_build_basic_posix(pxdesc,
2583 mode, scx->umask, isdir);
Steve Kondik2111ad72013-07-07 12:07:44 -07002584 }
2585 } else {
2586 securattr = getsecurityattr(scx->vol, dir_ni);
2587 if (securattr) {
2588 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
2589 securattr;
2590 gsid = (const SID*)&
2591 securattr[le32_to_cpu(phead->group)];
2592 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
2593#if OWNERFROMACL
2594 usid = ntfs_acl_owner(securattr);
2595 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2596 usid, gsid, TRUE);
2597 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2598#else
2599 usid = (const SID*)&
2600 securattr[le32_to_cpu(phead->owner)];
2601 pxdesc = ntfs_build_permissions_posix(scx->mapping,securattr,
2602 usid, gsid, TRUE);
2603 if (pxdesc && ntfs_same_sid(usid, adminsid)) {
2604 uid = find_tenant(scx, securattr);
2605 } else
2606 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
2607#endif
2608 if (pxdesc) {
2609 /*
2610 * Create a security id if there were none
2611 * and upgrade option is selected
2612 */
2613 if (!test_nino_flag(dir_ni, v3_Extensions)
2614 && (scx->vol->secure_flags
2615 & (1 << SECURITY_ADDSECURIDS))) {
2616 upgrade_secur_desc(scx->vol,
2617 securattr, dir_ni);
2618 /*
2619 * fetch owner and group for cacheing
2620 * if there is a securid
2621 */
2622 }
2623 if (test_nino_flag(dir_ni, v3_Extensions)) {
2624 enter_cache(scx, dir_ni, uid,
2625 gid, pxdesc);
2626 }
Steve Kondik79165c32015-11-09 19:43:00 -08002627 if (scx->vol->secure_flags
2628 & (1 << SECURITY_ACL))
2629 pydesc = ntfs_build_inherited_posix(
2630 pxdesc, mode,
2631 scx->umask, isdir);
2632 else
2633 pydesc = ntfs_build_basic_posix(
2634 pxdesc, mode,
2635 scx->umask, isdir);
Steve Kondik2111ad72013-07-07 12:07:44 -07002636 free(pxdesc);
2637 }
2638 free(securattr);
2639 }
2640 }
2641 return (pydesc);
2642}
2643
2644/*
2645 * Allocate a security_id for a file being created
2646 *
2647 * Returns zero if not possible (NTFS v3.x required)
2648 */
2649
2650le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2651 uid_t uid, gid_t gid, ntfs_inode *dir_ni,
2652 mode_t mode, BOOL isdir)
2653{
2654#if !FORCE_FORMAT_v1x
2655 const struct CACHED_SECURID *cached;
2656 struct CACHED_SECURID wanted;
2657 struct POSIX_SECURITY *pxdesc;
2658 char *newattr;
2659 int newattrsz;
2660 const SID *usid;
2661 const SID *gsid;
2662 BIGSID defusid;
2663 BIGSID defgsid;
2664 le32 securid;
2665#endif
2666
2667 securid = const_cpu_to_le32(0);
2668
2669#if !FORCE_FORMAT_v1x
2670
2671 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2672 if (pxdesc) {
2673 /* check whether target securid is known in cache */
2674
2675 wanted.uid = uid;
2676 wanted.gid = gid;
2677 wanted.dmode = pxdesc->mode & mode & 07777;
2678 if (isdir) wanted.dmode |= 0x10000;
2679 wanted.variable = (void*)pxdesc;
2680 wanted.varsize = sizeof(struct POSIX_SECURITY)
2681 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2682 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2683 scx->vol->securid_cache, GENERIC(&wanted),
2684 (cache_compare)compare);
2685 /* quite simple, if we are lucky */
2686 if (cached)
2687 securid = cached->securid;
2688
2689 /* not in cache : make sure we can create ids */
2690
2691 if (!cached && (scx->vol->major_ver >= 3)) {
2692 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2693 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2694 if (!usid || !gsid) {
2695 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2696 (int)uid, (int)gid);
2697 usid = gsid = adminsid;
2698 }
2699 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2700 isdir, usid, gsid);
2701 if (newattr) {
2702 newattrsz = ntfs_attr_size(newattr);
2703 securid = setsecurityattr(scx->vol,
2704 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2705 newattrsz);
2706 if (securid) {
2707 /* update cache, for subsequent use */
2708 wanted.securid = securid;
2709 ntfs_enter_cache(scx->vol->securid_cache,
2710 GENERIC(&wanted),
2711 (cache_compare)compare);
2712 }
2713 free(newattr);
2714 } else {
2715 /*
2716 * could not build new security attribute
2717 * errno set by ntfs_build_descr()
2718 */
2719 }
2720 }
2721 free(pxdesc);
2722 }
2723#endif
2724 return (securid);
2725}
2726
2727/*
2728 * Apply Posix inheritance to a newly created file
2729 * (for NTFS 1.x only : no securid)
2730 */
2731
2732int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx,
2733 ntfs_inode *ni, uid_t uid, gid_t gid,
2734 ntfs_inode *dir_ni, mode_t mode)
2735{
2736 struct POSIX_SECURITY *pxdesc;
2737 char *newattr;
2738 const SID *usid;
2739 const SID *gsid;
2740 BIGSID defusid;
2741 BIGSID defgsid;
2742 BOOL isdir;
2743 int res;
2744
2745 res = -1;
2746 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2747 pxdesc = inherit_posix(scx, dir_ni, mode, isdir);
2748 if (pxdesc) {
2749 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2750 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2751 if (!usid || !gsid) {
2752 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2753 (int)uid, (int)gid);
2754 usid = gsid = adminsid;
2755 }
2756 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2757 isdir, usid, gsid);
2758 if (newattr) {
2759 /* Adjust Windows read-only flag */
2760 res = update_secur_descr(scx->vol, newattr, ni);
2761 if (!res && !isdir) {
2762 if (mode & S_IWUSR)
2763 ni->flags &= ~FILE_ATTR_READONLY;
2764 else
2765 ni->flags |= FILE_ATTR_READONLY;
2766 }
2767#if CACHE_LEGACY_SIZE
2768 /* also invalidate legacy cache */
2769 if (isdir && !ni->security_id) {
2770 struct CACHED_PERMISSIONS_LEGACY legacy;
2771
2772 legacy.mft_no = ni->mft_no;
2773 legacy.variable = pxdesc;
2774 legacy.varsize = sizeof(struct POSIX_SECURITY)
2775 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2776 ntfs_invalidate_cache(scx->vol->legacy_cache,
2777 GENERIC(&legacy),
2778 (cache_compare)leg_compare,0);
2779 }
2780#endif
2781 free(newattr);
2782
2783 } else {
2784 /*
2785 * could not build new security attribute
2786 * errno set by ntfs_build_descr()
2787 */
2788 }
2789 }
2790 return (res);
2791}
2792
2793#else
2794
2795le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx,
2796 uid_t uid, gid_t gid, mode_t mode, BOOL isdir)
2797{
2798#if !FORCE_FORMAT_v1x
2799 const struct CACHED_SECURID *cached;
2800 struct CACHED_SECURID wanted;
2801 char *newattr;
2802 int newattrsz;
2803 const SID *usid;
2804 const SID *gsid;
2805 BIGSID defusid;
2806 BIGSID defgsid;
2807 le32 securid;
2808#endif
2809
2810 securid = const_cpu_to_le32(0);
2811
2812#if !FORCE_FORMAT_v1x
2813 /* check whether target securid is known in cache */
2814
2815 wanted.uid = uid;
2816 wanted.gid = gid;
2817 wanted.dmode = mode & 07777;
2818 if (isdir) wanted.dmode |= 0x10000;
2819 wanted.variable = (void*)NULL;
2820 wanted.varsize = 0;
2821 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2822 scx->vol->securid_cache, GENERIC(&wanted),
2823 (cache_compare)compare);
2824 /* quite simple, if we are lucky */
2825 if (cached)
2826 securid = cached->securid;
2827
2828 /* not in cache : make sure we can create ids */
2829
2830 if (!cached && (scx->vol->major_ver >= 3)) {
2831 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2832 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2833 if (!usid || !gsid) {
2834 ntfs_log_error("File created by an unmapped user/group %d/%d\n",
2835 (int)uid, (int)gid);
2836 usid = gsid = adminsid;
2837 }
2838 newattr = ntfs_build_descr(mode, isdir, usid, gsid);
2839 if (newattr) {
2840 newattrsz = ntfs_attr_size(newattr);
2841 securid = setsecurityattr(scx->vol,
2842 (const SECURITY_DESCRIPTOR_RELATIVE*)newattr,
2843 newattrsz);
2844 if (securid) {
2845 /* update cache, for subsequent use */
2846 wanted.securid = securid;
2847 ntfs_enter_cache(scx->vol->securid_cache,
2848 GENERIC(&wanted),
2849 (cache_compare)compare);
2850 }
2851 free(newattr);
2852 } else {
2853 /*
2854 * could not build new security attribute
2855 * errno set by ntfs_build_descr()
2856 */
2857 }
2858 }
2859#endif
2860 return (securid);
2861}
2862
2863#endif
2864
2865/*
2866 * Update ownership and mode of a file, reusing an existing
2867 * security descriptor when possible
2868 *
2869 * Returns zero if successful
2870 */
2871
2872#if POSIXACLS
2873int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2874 uid_t uid, gid_t gid, mode_t mode,
2875 struct POSIX_SECURITY *pxdesc)
2876#else
2877int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
2878 uid_t uid, gid_t gid, mode_t mode)
2879#endif
2880{
2881 int res;
2882 const struct CACHED_SECURID *cached;
2883 struct CACHED_SECURID wanted;
2884 char *newattr;
2885 const SID *usid;
2886 const SID *gsid;
2887 BIGSID defusid;
2888 BIGSID defgsid;
2889 BOOL isdir;
2890
2891 res = 0;
2892
2893 /* check whether target securid is known in cache */
2894
2895 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
2896 wanted.uid = uid;
2897 wanted.gid = gid;
2898 wanted.dmode = mode & 07777;
2899 if (isdir) wanted.dmode |= 0x10000;
2900#if POSIXACLS
2901 wanted.variable = (void*)pxdesc;
2902 if (pxdesc)
2903 wanted.varsize = sizeof(struct POSIX_SECURITY)
2904 + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE);
2905 else
2906 wanted.varsize = 0;
2907#else
2908 wanted.variable = (void*)NULL;
2909 wanted.varsize = 0;
2910#endif
2911 if (test_nino_flag(ni, v3_Extensions)) {
2912 cached = (const struct CACHED_SECURID*)ntfs_fetch_cache(
2913 scx->vol->securid_cache, GENERIC(&wanted),
2914 (cache_compare)compare);
2915 /* quite simple, if we are lucky */
2916 if (cached) {
2917 ni->security_id = cached->securid;
2918 NInoSetDirty(ni);
Steve Kondike68cb602016-08-28 00:45:36 -07002919 /* adjust Windows read-only flag */
2920 if (!isdir) {
2921 if (mode & S_IWUSR)
2922 ni->flags &= ~FILE_ATTR_READONLY;
2923 else
2924 ni->flags |= FILE_ATTR_READONLY;
2925 NInoFileNameSetDirty(ni);
2926 }
Steve Kondik2111ad72013-07-07 12:07:44 -07002927 }
2928 } else cached = (struct CACHED_SECURID*)NULL;
2929
2930 if (!cached) {
2931 /*
2932 * Do not use usid and gsid from former attributes,
2933 * but recompute them to get repeatable results
2934 * which can be kept in cache.
2935 */
2936 usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid);
2937 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid);
2938 if (!usid || !gsid) {
2939 ntfs_log_error("File made owned by an unmapped user/group %d/%d\n",
2940 uid, gid);
2941 usid = gsid = adminsid;
2942 }
2943#if POSIXACLS
2944 if (pxdesc)
2945 newattr = ntfs_build_descr_posix(scx->mapping, pxdesc,
2946 isdir, usid, gsid);
2947 else
2948 newattr = ntfs_build_descr(mode,
2949 isdir, usid, gsid);
2950#else
2951 newattr = ntfs_build_descr(mode,
2952 isdir, usid, gsid);
2953#endif
2954 if (newattr) {
2955 res = update_secur_descr(scx->vol, newattr, ni);
2956 if (!res) {
2957 /* adjust Windows read-only flag */
2958 if (!isdir) {
2959 if (mode & S_IWUSR)
2960 ni->flags &= ~FILE_ATTR_READONLY;
2961 else
2962 ni->flags |= FILE_ATTR_READONLY;
2963 NInoFileNameSetDirty(ni);
2964 }
2965 /* update cache, for subsequent use */
2966 if (test_nino_flag(ni, v3_Extensions)) {
2967 wanted.securid = ni->security_id;
2968 ntfs_enter_cache(scx->vol->securid_cache,
2969 GENERIC(&wanted),
2970 (cache_compare)compare);
2971 }
2972#if CACHE_LEGACY_SIZE
2973 /* also invalidate legacy cache */
2974 if (isdir && !ni->security_id) {
2975 struct CACHED_PERMISSIONS_LEGACY legacy;
2976
2977 legacy.mft_no = ni->mft_no;
2978#if POSIXACLS
2979 legacy.variable = wanted.variable;
2980 legacy.varsize = wanted.varsize;
2981#else
2982 legacy.variable = (void*)NULL;
2983 legacy.varsize = 0;
2984#endif
2985 ntfs_invalidate_cache(scx->vol->legacy_cache,
2986 GENERIC(&legacy),
2987 (cache_compare)leg_compare,0);
2988 }
2989#endif
2990 }
2991 free(newattr);
2992 } else {
2993 /*
2994 * could not build new security attribute
2995 * errno set by ntfs_build_descr()
2996 */
2997 res = -1;
2998 }
2999 }
3000 return (res);
3001}
3002
3003/*
3004 * Check whether user has ownership rights on a file
3005 *
3006 * Returns TRUE if allowed
3007 * if not, errno tells why
3008 */
3009
3010BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni)
3011{
3012 const struct CACHED_PERMISSIONS *cached;
3013 char *oldattr;
3014 const SID *usid;
3015 uid_t processuid;
3016 uid_t uid;
3017 BOOL gotowner;
3018 int allowed;
3019
3020 processuid = scx->uid;
3021/* TODO : use CAP_FOWNER process capability */
3022 /*
3023 * Always allow for root
3024 * Also always allow if no mapping has been defined
3025 */
3026 if (!scx->mapping[MAPUSERS] || !processuid)
3027 allowed = TRUE;
3028 else {
3029 gotowner = FALSE; /* default */
3030 /* get the owner, either from cache or from old attribute */
3031 cached = fetch_cache(scx, ni);
3032 if (cached) {
3033 uid = cached->uid;
3034 gotowner = TRUE;
3035 } else {
3036 oldattr = getsecurityattr(scx->vol, ni);
3037 if (oldattr) {
3038#if OWNERFROMACL
3039 usid = ntfs_acl_owner(oldattr);
3040#else
3041 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3042
3043 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3044 oldattr;
3045 usid = (const SID*)&oldattr
3046 [le32_to_cpu(phead->owner)];
3047#endif
3048 uid = ntfs_find_user(scx->mapping[MAPUSERS],
3049 usid);
3050 gotowner = TRUE;
3051 free(oldattr);
3052 }
3053 }
3054 allowed = FALSE;
3055 if (gotowner) {
3056/* TODO : use CAP_FOWNER process capability */
3057 if (!processuid || (processuid == uid))
3058 allowed = TRUE;
3059 else
3060 errno = EPERM;
3061 }
3062 }
3063 return (allowed);
3064}
3065
3066#ifdef HAVE_SETXATTR /* extended attributes interface required */
3067
3068#if POSIXACLS
3069
3070/*
3071 * Set a new access or default Posix ACL to a file
3072 * (or remove ACL if no input data)
3073 * Validity of input data is checked after merging
3074 *
3075 * Returns 0, or -1 if there is a problem which errno describes
3076 */
3077
3078int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3079 const char *name, const char *value, size_t size,
3080 int flags)
3081{
3082 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3083 const struct CACHED_PERMISSIONS *cached;
3084 char *oldattr;
3085 uid_t processuid;
3086 const SID *usid;
3087 const SID *gsid;
3088 uid_t uid;
3089 uid_t gid;
3090 int res;
3091 BOOL isdir;
3092 BOOL deflt;
3093 BOOL exist;
3094 int count;
3095 struct POSIX_SECURITY *oldpxdesc;
3096 struct POSIX_SECURITY *newpxdesc;
3097
3098 /* get the current pxsec, either from cache or from old attribute */
3099 res = -1;
3100 deflt = !strcmp(name,"system.posix_acl_default");
3101 if (size)
3102 count = (size - sizeof(struct POSIX_ACL)) / sizeof(struct POSIX_ACE);
3103 else
3104 count = 0;
3105 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
3106 newpxdesc = (struct POSIX_SECURITY*)NULL;
3107 if ((!value
3108 || (((const struct POSIX_ACL*)value)->version == POSIX_VERSION))
3109 && (!deflt || isdir || (!size && !value))) {
3110 cached = fetch_cache(scx, ni);
3111 if (cached) {
3112 uid = cached->uid;
3113 gid = cached->gid;
3114 oldpxdesc = cached->pxdesc;
3115 if (oldpxdesc) {
3116 newpxdesc = ntfs_replace_acl(oldpxdesc,
3117 (const struct POSIX_ACL*)value,count,deflt);
3118 }
3119 } else {
3120 oldattr = getsecurityattr(scx->vol, ni);
3121 if (oldattr) {
3122 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
3123#if OWNERFROMACL
3124 usid = ntfs_acl_owner(oldattr);
3125#else
3126 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
3127#endif
3128 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
3129 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3130 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3131 oldpxdesc = ntfs_build_permissions_posix(scx->mapping,
3132 oldattr, usid, gsid, isdir);
3133 if (oldpxdesc) {
3134 if (deflt)
3135 exist = oldpxdesc->defcnt > 0;
3136 else
3137 exist = oldpxdesc->acccnt > 3;
3138 if ((exist && (flags & XATTR_CREATE))
3139 || (!exist && (flags & XATTR_REPLACE))) {
3140 errno = (exist ? EEXIST : ENODATA);
3141 } else {
3142 newpxdesc = ntfs_replace_acl(oldpxdesc,
3143 (const struct POSIX_ACL*)value,count,deflt);
3144 }
3145 free(oldpxdesc);
3146 }
3147 free(oldattr);
3148 }
3149 }
3150 } else
3151 errno = EINVAL;
3152
3153 if (newpxdesc) {
3154 processuid = scx->uid;
3155/* TODO : use CAP_FOWNER process capability */
3156 if (!processuid || (uid == processuid)) {
3157 /*
3158 * clear setgid if file group does
3159 * not match process group
3160 */
3161 if (processuid && (gid != scx->gid)
3162 && !groupmember(scx, scx->uid, gid)) {
3163 newpxdesc->mode &= ~S_ISGID;
3164 }
3165 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3166 newpxdesc->mode, newpxdesc);
3167 } else
3168 errno = EPERM;
3169 free(newpxdesc);
3170 }
3171 return (res ? -1 : 0);
3172}
3173
3174/*
3175 * Remove a default Posix ACL from a file
3176 *
3177 * Returns 0, or -1 if there is a problem which errno describes
3178 */
3179
3180int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3181 const char *name)
3182{
3183 return (ntfs_set_posix_acl(scx, ni, name,
3184 (const char*)NULL, 0, 0));
3185}
3186
3187#endif
3188
3189/*
3190 * Set a new NTFS ACL to a file
3191 *
3192 * Returns 0, or -1 if there is a problem
3193 */
3194
3195int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3196 const char *value, size_t size, int flags)
3197{
3198 char *attr;
3199 int res;
3200
3201 res = -1;
3202 if ((size > 0)
3203 && !(flags & XATTR_CREATE)
3204 && ntfs_valid_descr(value,size)
3205 && (ntfs_attr_size(value) == size)) {
3206 /* need copying in order to write */
3207 attr = (char*)ntfs_malloc(size);
3208 if (attr) {
3209 memcpy(attr,value,size);
3210 res = update_secur_descr(scx->vol, attr, ni);
3211 /*
3212 * No need to invalidate standard caches :
3213 * the relation between a securid and
3214 * the associated protection is unchanged,
3215 * only the relation between a file and
3216 * its securid and protection is changed.
3217 */
3218#if CACHE_LEGACY_SIZE
3219 /*
3220 * we must however invalidate the legacy
3221 * cache, which is based on inode numbers.
3222 * For safety, invalidate even if updating
3223 * failed.
3224 */
3225 if ((ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3226 && !ni->security_id) {
3227 struct CACHED_PERMISSIONS_LEGACY legacy;
3228
3229 legacy.mft_no = ni->mft_no;
3230 legacy.variable = (char*)NULL;
3231 legacy.varsize = 0;
3232 ntfs_invalidate_cache(scx->vol->legacy_cache,
3233 GENERIC(&legacy),
3234 (cache_compare)leg_compare,0);
3235 }
3236#endif
3237 free(attr);
3238 } else
3239 errno = ENOMEM;
3240 } else
3241 errno = EINVAL;
3242 return (res ? -1 : 0);
3243}
3244
3245#endif /* HAVE_SETXATTR */
3246
3247/*
3248 * Set new permissions to a file
3249 * Checks user mapping has been defined before request for setting
3250 *
3251 * rejected if request is not originated by owner or root
3252 *
3253 * returns 0 on success
3254 * -1 on failure, with errno = EIO
3255 */
3256
3257int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode)
3258{
3259 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3260 const struct CACHED_PERMISSIONS *cached;
3261 char *oldattr;
3262 const SID *usid;
3263 const SID *gsid;
3264 uid_t processuid;
3265 uid_t uid;
3266 uid_t gid;
3267 int res;
3268#if POSIXACLS
3269 BOOL isdir;
3270 int pxsize;
3271 const struct POSIX_SECURITY *oldpxdesc;
3272 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3273#endif
3274
3275 /* get the current owner, either from cache or from old attribute */
3276 res = 0;
3277 cached = fetch_cache(scx, ni);
3278 if (cached) {
3279 uid = cached->uid;
3280 gid = cached->gid;
3281#if POSIXACLS
3282 oldpxdesc = cached->pxdesc;
3283 if (oldpxdesc) {
3284 /* must copy before merging */
3285 pxsize = sizeof(struct POSIX_SECURITY)
3286 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3287 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3288 if (newpxdesc) {
3289 memcpy(newpxdesc, oldpxdesc, pxsize);
3290 if (ntfs_merge_mode_posix(newpxdesc, mode))
3291 res = -1;
3292 } else
3293 res = -1;
3294 } else
3295 newpxdesc = (struct POSIX_SECURITY*)NULL;
3296#endif
3297 } else {
3298 oldattr = getsecurityattr(scx->vol, ni);
3299 if (oldattr) {
3300 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
3301#if OWNERFROMACL
3302 usid = ntfs_acl_owner(oldattr);
3303#else
3304 usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)];
3305#endif
3306 gsid = (const SID*)&oldattr[le32_to_cpu(phead->group)];
3307 uid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3308 gid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3309#if POSIXACLS
3310 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0);
3311 newpxdesc = ntfs_build_permissions_posix(scx->mapping,
3312 oldattr, usid, gsid, isdir);
3313 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3314 res = -1;
3315#endif
3316 free(oldattr);
3317 } else
3318 res = -1;
3319 }
3320
3321 if (!res) {
3322 processuid = scx->uid;
3323/* TODO : use CAP_FOWNER process capability */
3324 if (!processuid || (uid == processuid)) {
3325 /*
3326 * clear setgid if file group does
3327 * not match process group
3328 */
3329 if (processuid && (gid != scx->gid)
3330 && !groupmember(scx, scx->uid, gid))
3331 mode &= ~S_ISGID;
3332#if POSIXACLS
3333 if (newpxdesc) {
3334 newpxdesc->mode = mode;
3335 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3336 mode, newpxdesc);
3337 } else
3338 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3339 mode, newpxdesc);
3340#else
3341 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3342#endif
3343 } else {
3344 errno = EPERM;
3345 res = -1; /* neither owner nor root */
3346 }
3347 } else {
3348 /*
3349 * Should not happen : a default descriptor is generated
3350 * by getsecurityattr() when there are none
3351 */
3352 ntfs_log_error("File has no security descriptor\n");
3353 res = -1;
3354 errno = EIO;
3355 }
3356#if POSIXACLS
3357 if (newpxdesc) free(newpxdesc);
3358#endif
3359 return (res ? -1 : 0);
3360}
3361
3362/*
3363 * Create a default security descriptor for files whose descriptor
3364 * cannot be inherited
3365 */
3366
3367int ntfs_sd_add_everyone(ntfs_inode *ni)
3368{
3369 /* JPA SECURITY_DESCRIPTOR_ATTR *sd; */
3370 SECURITY_DESCRIPTOR_RELATIVE *sd;
3371 ACL *acl;
3372 ACCESS_ALLOWED_ACE *ace;
3373 SID *sid;
3374 int ret, sd_len;
3375
3376 /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
3377 /*
3378 * Calculate security descriptor length. We have 2 sub-authorities in
3379 * owner and group SIDs, but structure SID contain only one, so add
3380 * 4 bytes to every SID.
3381 */
3382 sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
3383 sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
3384 sd = (SECURITY_DESCRIPTOR_RELATIVE*)ntfs_calloc(sd_len);
3385 if (!sd)
3386 return -1;
3387
3388 sd->revision = SECURITY_DESCRIPTOR_REVISION;
3389 sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
3390
3391 sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
3392 sid->revision = SID_REVISION;
3393 sid->sub_authority_count = 2;
3394 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3395 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3396 sid->identifier_authority.value[5] = 5;
3397 sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
3398
3399 sid = (SID*)((u8*)sid + sizeof(SID) + 4);
3400 sid->revision = SID_REVISION;
3401 sid->sub_authority_count = 2;
3402 sid->sub_authority[0] = const_cpu_to_le32(SECURITY_BUILTIN_DOMAIN_RID);
3403 sid->sub_authority[1] = const_cpu_to_le32(DOMAIN_ALIAS_RID_ADMINS);
3404 sid->identifier_authority.value[5] = 5;
3405 sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
3406
3407 acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
3408 acl->revision = ACL_REVISION;
3409 acl->size = const_cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
3410 acl->ace_count = const_cpu_to_le16(1);
3411 sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
3412
3413 ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
3414 ace->type = ACCESS_ALLOWED_ACE_TYPE;
3415 ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
3416 ace->size = const_cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
3417 ace->mask = const_cpu_to_le32(0x1f01ff); /* FIXME */
3418 ace->sid.revision = SID_REVISION;
3419 ace->sid.sub_authority_count = 1;
3420 ace->sid.sub_authority[0] = const_cpu_to_le32(0);
3421 ace->sid.identifier_authority.value[5] = 1;
3422
3423 ret = ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, (u8*)sd,
3424 sd_len);
3425 if (ret)
3426 ntfs_log_perror("Failed to add initial SECURITY_DESCRIPTOR");
3427
3428 free(sd);
3429 return ret;
3430}
3431
3432/*
3433 * Check whether user can access a file in a specific way
3434 *
3435 * Returns 1 if access is allowed, including user is root or no
3436 * user mapping defined
3437 * 2 if sticky and accesstype is S_IWRITE + S_IEXEC + S_ISVTX
3438 * 0 and sets errno if there is a problem or if access
3439 * is not allowed
3440 *
3441 * This is used for Posix ACL and checking creation of DOS file names
3442 */
3443
3444int ntfs_allowed_access(struct SECURITY_CONTEXT *scx,
3445 ntfs_inode *ni,
3446 int accesstype) /* access type required (S_Ixxx values) */
3447{
3448 int perm;
3449 int res;
3450 int allow;
3451 struct stat stbuf;
3452
3453 /*
3454 * Always allow for root unless execution is requested.
3455 * (was checked by fuse until kernel 2.6.29)
3456 * Also always allow if no mapping has been defined
3457 */
3458 if (!scx->mapping[MAPUSERS]
3459 || (!scx->uid
3460 && (!(accesstype & S_IEXEC)
3461 || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY))))
3462 allow = 1;
3463 else {
3464 perm = ntfs_get_perm(scx, ni, accesstype);
3465 if (perm >= 0) {
3466 res = EACCES;
3467 switch (accesstype) {
3468 case S_IEXEC:
3469 allow = (perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
3470 break;
3471 case S_IWRITE:
3472 allow = (perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0;
3473 break;
3474 case S_IWRITE + S_IEXEC:
3475 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3476 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3477 break;
3478 case S_IREAD:
3479 allow = (perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0;
3480 break;
3481 case S_IREAD + S_IEXEC:
3482 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3483 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3484 break;
3485 case S_IREAD + S_IWRITE:
3486 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3487 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0);
3488 break;
3489 case S_IWRITE + S_IEXEC + S_ISVTX:
3490 if (perm & S_ISVTX) {
3491 if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3492 && (stbuf.st_uid == scx->uid))
3493 allow = 1;
3494 else
3495 allow = 2;
3496 } else
3497 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3498 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3499 break;
3500 case S_IREAD + S_IWRITE + S_IEXEC:
3501 allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0)
3502 && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3503 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3504 break;
3505 default :
3506 res = EINVAL;
3507 allow = 0;
3508 break;
3509 }
3510 if (!allow)
3511 errno = res;
3512 } else
3513 allow = 0;
3514 }
3515 return (allow);
Steve Kondik2111ad72013-07-07 12:07:44 -07003516}
3517
3518/*
3519 * Check whether user can create a file (or directory)
3520 *
3521 * Returns TRUE if access is allowed,
3522 * Also returns the gid and dsetgid applicable to the created file
3523 */
3524
3525int ntfs_allowed_create(struct SECURITY_CONTEXT *scx,
3526 ntfs_inode *dir_ni, gid_t *pgid, mode_t *pdsetgid)
3527{
3528 int perm;
3529 int res;
3530 int allow;
3531 struct stat stbuf;
3532
3533 /*
3534 * Always allow for root.
3535 * Also always allow if no mapping has been defined
3536 */
3537 if (!scx->mapping[MAPUSERS])
3538 perm = 0777;
3539 else
3540 perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
3541 if (!scx->mapping[MAPUSERS]
3542 || !scx->uid) {
3543 allow = 1;
3544 } else {
3545 perm = ntfs_get_perm(scx, dir_ni, S_IWRITE + S_IEXEC);
3546 if (perm >= 0) {
3547 res = EACCES;
3548 allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0)
3549 && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0);
3550 if (!allow)
3551 errno = res;
3552 } else
3553 allow = 0;
3554 }
3555 *pgid = scx->gid;
3556 *pdsetgid = 0;
3557 /* return directory group if S_ISGID is set */
3558 if (allow && (perm & S_ISGID)) {
3559 if (ntfs_get_owner_mode(scx, dir_ni, &stbuf) >= 0) {
3560 *pdsetgid = stbuf.st_mode & S_ISGID;
3561 if (perm & S_ISGID)
3562 *pgid = stbuf.st_gid;
3563 }
3564 }
3565 return (allow);
3566}
3567
3568#if 0 /* not needed any more */
3569
3570/*
3571 * Check whether user can access the parent directory
3572 * of a file in a specific way
3573 *
3574 * Returns true if access is allowed, including user is root and
3575 * no user mapping defined
3576 *
3577 * Sets errno if there is a problem or if not allowed
3578 *
3579 * This is used for Posix ACL and checking creation of DOS file names
3580 */
3581
3582BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx,
3583 const char *path, int accesstype)
3584{
3585 int allow;
3586 char *dirpath;
3587 char *name;
3588 ntfs_inode *ni;
3589 ntfs_inode *dir_ni;
3590 struct stat stbuf;
3591
3592 allow = 0;
3593 dirpath = strdup(path);
3594 if (dirpath) {
3595 /* the root of file system is seen as a parent of itself */
3596 /* is that correct ? */
3597 name = strrchr(dirpath, '/');
3598 *name = 0;
3599 dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath);
3600 if (dir_ni) {
3601 allow = ntfs_allowed_access(scx,
3602 dir_ni, accesstype);
3603 ntfs_inode_close(dir_ni);
3604 /*
3605 * for an not-owned sticky directory, have to
3606 * check whether file itself is owned
3607 */
3608 if ((accesstype == (S_IWRITE + S_IEXEC + S_ISVTX))
3609 && (allow == 2)) {
3610 ni = ntfs_pathname_to_inode(scx->vol, NULL,
3611 path);
3612 allow = FALSE;
3613 if (ni) {
3614 allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0)
3615 && (stbuf.st_uid == scx->uid);
3616 ntfs_inode_close(ni);
3617 }
3618 }
3619 }
3620 free(dirpath);
3621 }
3622 return (allow); /* errno is set if not allowed */
3623}
3624
3625#endif
3626
3627/*
3628 * Define a new owner/group to a file
3629 *
3630 * returns zero if successful
3631 */
3632
3633int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3634 uid_t uid, gid_t gid)
3635{
3636 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3637 const struct CACHED_PERMISSIONS *cached;
3638 char *oldattr;
3639 const SID *usid;
3640 const SID *gsid;
3641 uid_t fileuid;
3642 uid_t filegid;
3643 mode_t mode;
3644 int perm;
3645 BOOL isdir;
3646 int res;
3647#if POSIXACLS
3648 struct POSIX_SECURITY *pxdesc;
3649 BOOL pxdescbuilt = FALSE;
3650#endif
3651
3652 res = 0;
3653 /* get the current owner and mode from cache or security attributes */
3654 oldattr = (char*)NULL;
3655 cached = fetch_cache(scx,ni);
3656 if (cached) {
3657 fileuid = cached->uid;
3658 filegid = cached->gid;
3659 mode = cached->mode;
3660#if POSIXACLS
3661 pxdesc = cached->pxdesc;
3662 if (!pxdesc)
3663 res = -1;
3664#endif
3665 } else {
3666 fileuid = 0;
3667 filegid = 0;
3668 mode = 0;
3669 oldattr = getsecurityattr(scx->vol, ni);
3670 if (oldattr) {
3671 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3672 != const_cpu_to_le16(0);
3673 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3674 oldattr;
3675 gsid = (const SID*)
3676 &oldattr[le32_to_cpu(phead->group)];
3677#if OWNERFROMACL
3678 usid = ntfs_acl_owner(oldattr);
3679#else
3680 usid = (const SID*)
3681 &oldattr[le32_to_cpu(phead->owner)];
3682#endif
3683#if POSIXACLS
3684 pxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3685 usid, gsid, isdir);
3686 if (pxdesc) {
3687 pxdescbuilt = TRUE;
3688 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3689 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3690 mode = perm = pxdesc->mode;
3691 } else
3692 res = -1;
3693#else
3694 mode = perm = ntfs_build_permissions(oldattr,
3695 usid, gsid, isdir);
3696 if (perm >= 0) {
3697 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3698 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3699 } else
3700 res = -1;
3701#endif
3702 free(oldattr);
3703 } else
3704 res = -1;
3705 }
3706 if (!res) {
3707 /* check requested by root */
3708 /* or chgrp requested by owner to an owned group */
3709 if (!scx->uid
3710 || ((((int)uid < 0) || (uid == fileuid))
3711 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3712 && (fileuid == scx->uid))) {
3713 /* replace by the new usid and gsid */
3714 /* or reuse old gid and sid for cacheing */
3715 if ((int)uid < 0)
3716 uid = fileuid;
3717 if ((int)gid < 0)
3718 gid = filegid;
3719#if !defined(__sun) || !defined (__SVR4)
3720 /* clear setuid and setgid if owner has changed */
3721 /* unless request originated by root */
3722 if (uid && (fileuid != uid))
3723 mode &= 01777;
3724#endif
3725#if POSIXACLS
3726 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3727 mode, pxdesc);
3728#else
3729 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3730#endif
3731 } else {
3732 res = -1; /* neither owner nor root */
3733 errno = EPERM;
3734 }
3735#if POSIXACLS
3736 if (pxdescbuilt)
3737 free(pxdesc);
3738#endif
3739 } else {
3740 /*
3741 * Should not happen : a default descriptor is generated
3742 * by getsecurityattr() when there are none
3743 */
3744 ntfs_log_error("File has no security descriptor\n");
3745 res = -1;
3746 errno = EIO;
3747 }
3748 return (res ? -1 : 0);
3749}
3750
3751/*
3752 * Define new owner/group and mode to a file
3753 *
3754 * returns zero if successful
3755 */
3756
3757int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni,
3758 uid_t uid, gid_t gid, const mode_t mode)
3759{
3760 const struct CACHED_PERMISSIONS *cached;
3761 char *oldattr;
3762 uid_t fileuid;
3763 uid_t filegid;
3764 int res;
3765#if POSIXACLS
3766 const SECURITY_DESCRIPTOR_RELATIVE *phead;
3767 const SID *usid;
3768 const SID *gsid;
3769 BOOL isdir;
3770 const struct POSIX_SECURITY *oldpxdesc;
3771 struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL;
3772 int pxsize;
3773#endif
3774
3775 res = 0;
3776 /* get the current owner and mode from cache or security attributes */
3777 oldattr = (char*)NULL;
3778 cached = fetch_cache(scx,ni);
3779 if (cached) {
3780 fileuid = cached->uid;
3781 filegid = cached->gid;
3782#if POSIXACLS
3783 oldpxdesc = cached->pxdesc;
3784 if (oldpxdesc) {
3785 /* must copy before merging */
3786 pxsize = sizeof(struct POSIX_SECURITY)
3787 + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE);
3788 newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize);
3789 if (newpxdesc) {
3790 memcpy(newpxdesc, oldpxdesc, pxsize);
3791 if (ntfs_merge_mode_posix(newpxdesc, mode))
3792 res = -1;
3793 } else
3794 res = -1;
3795 }
3796#endif
3797 } else {
3798 fileuid = 0;
3799 filegid = 0;
3800 oldattr = getsecurityattr(scx->vol, ni);
3801 if (oldattr) {
3802#if POSIXACLS
3803 isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
3804 != const_cpu_to_le16(0);
3805 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)
3806 oldattr;
3807 gsid = (const SID*)
3808 &oldattr[le32_to_cpu(phead->group)];
3809#if OWNERFROMACL
3810 usid = ntfs_acl_owner(oldattr);
3811#else
3812 usid = (const SID*)
3813 &oldattr[le32_to_cpu(phead->owner)];
3814#endif
3815 newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr,
3816 usid, gsid, isdir);
3817 if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode))
3818 res = -1;
3819 else {
3820 fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid);
3821 filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid);
3822 }
3823#endif
3824 free(oldattr);
3825 } else
3826 res = -1;
3827 }
3828 if (!res) {
3829 /* check requested by root */
3830 /* or chgrp requested by owner to an owned group */
3831 if (!scx->uid
3832 || ((((int)uid < 0) || (uid == fileuid))
3833 && ((gid == scx->gid) || groupmember(scx, scx->uid, gid))
3834 && (fileuid == scx->uid))) {
3835 /* replace by the new usid and gsid */
3836 /* or reuse old gid and sid for cacheing */
3837 if ((int)uid < 0)
3838 uid = fileuid;
3839 if ((int)gid < 0)
3840 gid = filegid;
3841#if POSIXACLS
3842 res = ntfs_set_owner_mode(scx, ni, uid, gid,
3843 mode, newpxdesc);
3844#else
3845 res = ntfs_set_owner_mode(scx, ni, uid, gid, mode);
3846#endif
3847 } else {
3848 res = -1; /* neither owner nor root */
3849 errno = EPERM;
3850 }
3851 } else {
3852 /*
3853 * Should not happen : a default descriptor is generated
3854 * by getsecurityattr() when there are none
3855 */
3856 ntfs_log_error("File has no security descriptor\n");
3857 res = -1;
3858 errno = EIO;
3859 }
3860#if POSIXACLS
3861 free(newpxdesc);
3862#endif
3863 return (res ? -1 : 0);
3864}
3865
3866/*
3867 * Build a security id for a descriptor inherited from
3868 * parent directory the Windows way
3869 */
3870
3871static le32 build_inherited_id(struct SECURITY_CONTEXT *scx,
3872 const char *parentattr, BOOL fordir)
3873{
3874 const SECURITY_DESCRIPTOR_RELATIVE *pphead;
3875 const ACL *ppacl;
3876 const SID *usid;
3877 const SID *gsid;
3878 BIGSID defusid;
3879 BIGSID defgsid;
3880 int offpacl;
Steve Kondik2111ad72013-07-07 12:07:44 -07003881 int offgroup;
3882 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
3883 ACL *pnacl;
3884 int parentattrsz;
3885 char *newattr;
3886 int newattrsz;
3887 int aclsz;
3888 int usidsz;
3889 int gsidsz;
3890 int pos;
3891 le32 securid;
3892
3893 parentattrsz = ntfs_attr_size(parentattr);
3894 pphead = (const SECURITY_DESCRIPTOR_RELATIVE*)parentattr;
3895 if (scx->mapping[MAPUSERS]) {
3896 usid = ntfs_find_usid(scx->mapping[MAPUSERS], scx->uid, (SID*)&defusid);
3897 gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS], scx->gid, (SID*)&defgsid);
Steve Kondik79165c32015-11-09 19:43:00 -08003898#if OWNERFROMACL
3899 /* Get approximation of parent owner when cannot map */
3900 if (!gsid)
3901 gsid = adminsid;
3902 if (!usid) {
3903 usid = ntfs_acl_owner(parentattr);
3904 if (!ntfs_is_user_sid(gsid))
3905 gsid = usid;
3906 }
3907#else
3908 /* Define owner as root when cannot map */
Steve Kondik2111ad72013-07-07 12:07:44 -07003909 if (!usid)
3910 usid = adminsid;
3911 if (!gsid)
3912 gsid = adminsid;
Steve Kondik79165c32015-11-09 19:43:00 -08003913#endif
Steve Kondik2111ad72013-07-07 12:07:44 -07003914 } else {
3915 /*
Steve Kondik79165c32015-11-09 19:43:00 -08003916 * If there is no user mapping and this is not a root
3917 * user, we have to get owner and group from somewhere,
3918 * and the parent directory has to contribute.
Steve Kondik2111ad72013-07-07 12:07:44 -07003919 * Windows never has to do that, because it can always
3920 * rely on a user mapping
3921 */
Steve Kondik79165c32015-11-09 19:43:00 -08003922 if (!scx->uid)
3923 usid = adminsid;
3924 else {
3925#if OWNERFROMACL
3926 usid = ntfs_acl_owner(parentattr);
3927#else
3928 int offowner;
3929
3930 offowner = le32_to_cpu(pphead->owner);
3931 usid = (const SID*)&parentattr[offowner];
3932#endif
3933 }
3934 if (!scx->gid)
3935 gsid = adminsid;
3936 else {
3937 offgroup = le32_to_cpu(pphead->group);
3938 gsid = (const SID*)&parentattr[offgroup];
3939 }
Steve Kondik2111ad72013-07-07 12:07:44 -07003940 }
3941 /*
3942 * new attribute is smaller than parent's
3943 * except for differences in SIDs which appear in
3944 * owner, group and possible grants and denials in
3945 * generic creator-owner and creator-group ACEs.
3946 * For directories, an ACE may be duplicated for
3947 * access and inheritance, so we double the count.
3948 */
3949 usidsz = ntfs_sid_size(usid);
3950 gsidsz = ntfs_sid_size(gsid);
3951 newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
3952 if (fordir)
3953 newattrsz *= 2;
3954 newattr = (char*)ntfs_malloc(newattrsz);
3955 if (newattr) {
3956 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
3957 pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
3958 pnhead->alignment = 0;
3959 pnhead->control = (pphead->control
3960 & (SE_DACL_AUTO_INHERITED | SE_SACL_AUTO_INHERITED))
3961 | SE_SELF_RELATIVE;
3962 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
3963 /*
3964 * locate and inherit DACL
3965 * do not test SE_DACL_PRESENT (wrong for "DR Watson")
3966 */
3967 pnhead->dacl = const_cpu_to_le32(0);
3968 if (pphead->dacl) {
3969 offpacl = le32_to_cpu(pphead->dacl);
3970 ppacl = (const ACL*)&parentattr[offpacl];
3971 pnacl = (ACL*)&newattr[pos];
3972 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
3973 fordir, pphead->control
3974 & SE_DACL_AUTO_INHERITED);
3975 if (aclsz) {
3976 pnhead->dacl = cpu_to_le32(pos);
3977 pos += aclsz;
3978 pnhead->control |= SE_DACL_PRESENT;
3979 }
3980 }
3981 /*
3982 * locate and inherit SACL
3983 */
3984 pnhead->sacl = const_cpu_to_le32(0);
3985 if (pphead->sacl) {
3986 offpacl = le32_to_cpu(pphead->sacl);
3987 ppacl = (const ACL*)&parentattr[offpacl];
3988 pnacl = (ACL*)&newattr[pos];
3989 aclsz = ntfs_inherit_acl(ppacl, pnacl, usid, gsid,
3990 fordir, pphead->control
3991 & SE_SACL_AUTO_INHERITED);
3992 if (aclsz) {
3993 pnhead->sacl = cpu_to_le32(pos);
3994 pos += aclsz;
3995 pnhead->control |= SE_SACL_PRESENT;
3996 }
3997 }
3998 /*
3999 * inherit or redefine owner
4000 */
4001 memcpy(&newattr[pos],usid,usidsz);
4002 pnhead->owner = cpu_to_le32(pos);
4003 pos += usidsz;
4004 /*
4005 * inherit or redefine group
4006 */
4007 memcpy(&newattr[pos],gsid,gsidsz);
4008 pnhead->group = cpu_to_le32(pos);
4009 pos += gsidsz;
4010 securid = setsecurityattr(scx->vol,
4011 (SECURITY_DESCRIPTOR_RELATIVE*)newattr, pos);
4012 free(newattr);
4013 } else
4014 securid = const_cpu_to_le32(0);
4015 return (securid);
4016}
4017
4018/*
4019 * Get an inherited security id
4020 *
4021 * For Windows compatibility, the normal initial permission setting
4022 * may be inherited from the parent directory instead of being
4023 * defined by the creation arguments.
4024 *
4025 * The following creates an inherited id for that purpose.
4026 *
4027 * Note : the owner and group of parent directory are also
4028 * inherited (which is not the case on Windows) if no user mapping
4029 * is defined.
4030 *
4031 * Returns the inherited id, or zero if not possible (eg on NTFS 1.x)
4032 */
4033
4034le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx,
4035 ntfs_inode *dir_ni, BOOL fordir)
4036{
4037 struct CACHED_PERMISSIONS *cached;
4038 char *parentattr;
4039 le32 securid;
4040
4041 securid = const_cpu_to_le32(0);
4042 cached = (struct CACHED_PERMISSIONS*)NULL;
4043 /*
Steve Kondik79165c32015-11-09 19:43:00 -08004044 * Try to get inherited id from cache, possible when
4045 * the current process owns the parent directory
Steve Kondik2111ad72013-07-07 12:07:44 -07004046 */
4047 if (test_nino_flag(dir_ni, v3_Extensions)
4048 && dir_ni->security_id) {
4049 cached = fetch_cache(scx, dir_ni);
Steve Kondik79165c32015-11-09 19:43:00 -08004050 if (cached
4051 && (cached->uid == scx->uid) && (cached->gid == scx->gid))
Steve Kondik2111ad72013-07-07 12:07:44 -07004052 securid = (fordir ? cached->inh_dirid
4053 : cached->inh_fileid);
4054 }
4055 /*
4056 * Not cached or not available in cache, compute it all
4057 * Note : if parent directory has no id, it is not cacheable
4058 */
4059 if (!securid) {
4060 parentattr = getsecurityattr(scx->vol, dir_ni);
4061 if (parentattr) {
4062 securid = build_inherited_id(scx,
4063 parentattr, fordir);
4064 free(parentattr);
4065 /*
4066 * Store the result into cache for further use
Steve Kondik79165c32015-11-09 19:43:00 -08004067 * if the current process owns the parent directory
Steve Kondik2111ad72013-07-07 12:07:44 -07004068 */
4069 if (securid) {
4070 cached = fetch_cache(scx, dir_ni);
Steve Kondik79165c32015-11-09 19:43:00 -08004071 if (cached
4072 && (cached->uid == scx->uid)
4073 && (cached->gid == scx->gid)) {
Steve Kondik2111ad72013-07-07 12:07:44 -07004074 if (fordir)
4075 cached->inh_dirid = securid;
4076 else
4077 cached->inh_fileid = securid;
4078 }
4079 }
4080 }
4081 }
4082 return (securid);
4083}
4084
4085/*
4086 * Link a group to a member of group
4087 *
4088 * Returns 0 if OK, -1 (and errno set) if error
4089 */
4090
4091static int link_single_group(struct MAPPING *usermapping, struct passwd *user,
4092 gid_t gid)
4093{
4094 struct group *group;
4095 char **grmem;
4096 int grcnt;
4097 gid_t *groups;
4098 int res;
4099
4100 res = 0;
4101 group = getgrgid(gid);
4102 if (group && group->gr_mem) {
4103 grcnt = usermapping->grcnt;
4104 groups = usermapping->groups;
4105 grmem = group->gr_mem;
4106 while (*grmem && strcmp(user->pw_name, *grmem))
4107 grmem++;
4108 if (*grmem) {
4109 if (!grcnt)
4110 groups = (gid_t*)malloc(sizeof(gid_t));
4111 else
4112 groups = (gid_t*)realloc(groups,
4113 (grcnt+1)*sizeof(gid_t));
4114 if (groups)
4115 groups[grcnt++] = gid;
4116 else {
4117 res = -1;
4118 errno = ENOMEM;
4119 }
4120 }
4121 usermapping->grcnt = grcnt;
4122 usermapping->groups = groups;
4123 }
4124 return (res);
4125}
4126
4127
4128/*
4129 * Statically link group to users
4130 * This is based on groups defined in /etc/group and does not take
4131 * the groups dynamically set by setgroups() nor any changes in
4132 * /etc/group into account
4133 *
4134 * Only mapped groups and root group are linked to mapped users
4135 *
4136 * Returns 0 if OK, -1 (and errno set) if error
4137 *
4138 */
4139
4140static int link_group_members(struct SECURITY_CONTEXT *scx)
4141{
4142 struct MAPPING *usermapping;
4143 struct MAPPING *groupmapping;
4144 struct passwd *user;
4145 int res;
4146
4147 res = 0;
4148 for (usermapping=scx->mapping[MAPUSERS]; usermapping && !res;
4149 usermapping=usermapping->next) {
4150 usermapping->grcnt = 0;
4151 usermapping->groups = (gid_t*)NULL;
4152 user = getpwuid(usermapping->xid);
4153 if (user && user->pw_name) {
4154 for (groupmapping=scx->mapping[MAPGROUPS];
4155 groupmapping && !res;
4156 groupmapping=groupmapping->next) {
4157 if (link_single_group(usermapping, user,
4158 groupmapping->xid))
4159 res = -1;
4160 }
4161 if (!res && link_single_group(usermapping,
4162 user, (gid_t)0))
4163 res = -1;
4164 }
4165 }
4166 return (res);
4167}
4168
4169/*
4170 * Apply default single user mapping
4171 * returns zero if successful
4172 */
4173
4174static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx,
4175 uid_t uid, gid_t gid, const SID *usid)
4176{
4177 struct MAPPING *usermapping;
4178 struct MAPPING *groupmapping;
4179 SID *sid;
4180 int sidsz;
4181 int res;
4182
4183 res = -1;
4184 sidsz = ntfs_sid_size(usid);
4185 sid = (SID*)ntfs_malloc(sidsz);
4186 if (sid) {
4187 memcpy(sid,usid,sidsz);
4188 usermapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
4189 if (usermapping) {
4190 groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING));
4191 if (groupmapping) {
4192 usermapping->sid = sid;
4193 usermapping->xid = uid;
4194 usermapping->next = (struct MAPPING*)NULL;
4195 groupmapping->sid = sid;
4196 groupmapping->xid = gid;
4197 groupmapping->next = (struct MAPPING*)NULL;
4198 scx->mapping[MAPUSERS] = usermapping;
4199 scx->mapping[MAPGROUPS] = groupmapping;
4200 res = 0;
4201 }
4202 }
4203 }
4204 return (res);
4205}
4206
4207/*
4208 * Make sure there are no ambiguous mapping
4209 * Ambiguous mapping may lead to undesired configurations and
4210 * we had rather be safe until the consequences are understood
4211 */
4212
4213#if 0 /* not activated for now */
4214
4215static BOOL check_mapping(const struct MAPPING *usermapping,
4216 const struct MAPPING *groupmapping)
4217{
4218 const struct MAPPING *mapping1;
4219 const struct MAPPING *mapping2;
4220 BOOL ambiguous;
4221
4222 ambiguous = FALSE;
4223 for (mapping1=usermapping; mapping1; mapping1=mapping1->next)
4224 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
4225 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
4226 if (mapping1->xid != mapping2->xid)
4227 ambiguous = TRUE;
4228 } else {
4229 if (mapping1->xid == mapping2->xid)
4230 ambiguous = TRUE;
4231 }
4232 for (mapping1=groupmapping; mapping1; mapping1=mapping1->next)
4233 for (mapping2=mapping1->next; mapping2; mapping1=mapping2->next)
4234 if (ntfs_same_sid(mapping1->sid,mapping2->sid)) {
4235 if (mapping1->xid != mapping2->xid)
4236 ambiguous = TRUE;
4237 } else {
4238 if (mapping1->xid == mapping2->xid)
4239 ambiguous = TRUE;
4240 }
4241 return (ambiguous);
4242}
4243
4244#endif
4245
4246#if 0 /* not used any more */
4247
4248/*
4249 * Try and apply default single user mapping
4250 * returns zero if successful
4251 */
4252
4253static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx)
4254{
4255 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4256 ntfs_inode *ni;
4257 char *securattr;
4258 const SID *usid;
4259 int res;
4260
4261 res = -1;
4262 ni = ntfs_pathname_to_inode(scx->vol, NULL, "/.");
4263 if (ni) {
4264 securattr = getsecurityattr(scx->vol, ni);
4265 if (securattr) {
4266 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
4267 usid = (SID*)&securattr[le32_to_cpu(phead->owner)];
4268 if (ntfs_is_user_sid(usid))
4269 res = ntfs_do_default_mapping(scx,
4270 scx->uid, scx->gid, usid);
4271 free(securattr);
4272 }
4273 ntfs_inode_close(ni);
4274 }
4275 return (res);
4276}
4277
4278#endif
4279
4280/*
4281 * Basic read from a user mapping file on another volume
4282 */
4283
4284static int basicread(void *fileid, char *buf, size_t size, off_t offs __attribute__((unused)))
4285{
4286 return (read(*(int*)fileid, buf, size));
4287}
4288
4289
4290/*
4291 * Read from a user mapping file on current NTFS partition
4292 */
4293
4294static int localread(void *fileid, char *buf, size_t size, off_t offs)
4295{
4296 return (ntfs_attr_data_read((ntfs_inode*)fileid,
4297 AT_UNNAMED, 0, buf, size, offs));
4298}
4299
4300/*
4301 * Build the user mapping
4302 * - according to a mapping file if defined (or default present),
4303 * - or try default single user mapping if possible
4304 *
4305 * The mapping is specific to a mounted device
4306 * No locking done, mounting assumed non multithreaded
4307 *
4308 * returns zero if mapping is successful
4309 * (failure should not be interpreted as an error)
4310 */
4311
4312int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path,
4313 BOOL allowdef)
4314{
4315 struct MAPLIST *item;
4316 struct MAPLIST *firstitem;
4317 struct MAPPING *usermapping;
4318 struct MAPPING *groupmapping;
4319 ntfs_inode *ni;
4320 int fd;
4321 static struct {
4322 u8 revision;
4323 u8 levels;
4324 be16 highbase;
4325 be32 lowbase;
4326 le32 level1;
4327 le32 level2;
4328 le32 level3;
4329 le32 level4;
4330 le32 level5;
4331 } defmap = {
4332 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5),
4333 const_cpu_to_le32(21),
4334 const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2),
4335 const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE)
4336 } ;
4337
4338 /* be sure not to map anything until done */
4339 scx->mapping[MAPUSERS] = (struct MAPPING*)NULL;
4340 scx->mapping[MAPGROUPS] = (struct MAPPING*)NULL;
4341
4342 if (!usermap_path) usermap_path = MAPPINGFILE;
4343 if (usermap_path[0] == '/') {
4344 fd = open(usermap_path,O_RDONLY);
4345 if (fd > 0) {
4346 firstitem = ntfs_read_mapping(basicread, (void*)&fd);
4347 close(fd);
4348 } else
4349 firstitem = (struct MAPLIST*)NULL;
4350 } else {
4351 ni = ntfs_pathname_to_inode(scx->vol, NULL, usermap_path);
4352 if (ni) {
4353 firstitem = ntfs_read_mapping(localread, ni);
4354 ntfs_inode_close(ni);
4355 } else
4356 firstitem = (struct MAPLIST*)NULL;
4357 }
4358
4359
4360 if (firstitem) {
4361 usermapping = ntfs_do_user_mapping(firstitem);
4362 groupmapping = ntfs_do_group_mapping(firstitem);
4363 if (usermapping && groupmapping) {
4364 scx->mapping[MAPUSERS] = usermapping;
4365 scx->mapping[MAPGROUPS] = groupmapping;
4366 } else
4367 ntfs_log_error("There were no valid user or no valid group\n");
4368 /* now we can free the memory copy of input text */
4369 /* and rely on internal representation */
4370 while (firstitem) {
4371 item = firstitem->next;
4372 free(firstitem);
4373 firstitem = item;
4374 }
4375 } else {
4376 /* no mapping file, try a default mapping */
4377 if (allowdef) {
4378 if (!ntfs_do_default_mapping(scx,
4379 0, 0, (const SID*)&defmap))
4380 ntfs_log_info("Using default user mapping\n");
4381 }
4382 }
4383 return (!scx->mapping[MAPUSERS] || link_group_members(scx));
4384}
4385
4386#ifdef HAVE_SETXATTR /* extended attributes interface required */
4387
4388/*
4389 * Get the ntfs attribute into an extended attribute
4390 * The attribute is returned according to cpu endianness
4391 */
4392
4393int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size)
4394{
4395 u32 attrib;
4396 size_t outsize;
4397
4398 outsize = 0; /* default to no data and no error */
4399 if (ni) {
4400 attrib = le32_to_cpu(ni->flags);
4401 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4402 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4403 else
4404 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
4405 if (!attrib)
4406 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
4407 outsize = sizeof(FILE_ATTR_FLAGS);
4408 if (size >= outsize) {
4409 if (value)
4410 memcpy(value,&attrib,outsize);
4411 else
4412 errno = EINVAL;
4413 }
4414 }
4415 return (outsize ? (int)outsize : -errno);
4416}
4417
4418/*
4419 * Return the ntfs attribute into an extended attribute
4420 * The attribute is expected according to cpu endianness
4421 *
4422 * Returns 0, or -1 if there is a problem
4423 */
4424
4425int ntfs_set_ntfs_attrib(ntfs_inode *ni,
4426 const char *value, size_t size, int flags)
4427{
4428 u32 attrib;
4429 le32 settable;
4430 ATTR_FLAGS dirflags;
4431 int res;
4432
4433 res = -1;
4434 if (ni && value && (size >= sizeof(FILE_ATTR_FLAGS))) {
4435 if (!(flags & XATTR_CREATE)) {
4436 /* copy to avoid alignment problems */
4437 memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
4438 settable = FILE_ATTR_SETTABLE;
4439 res = 0;
4440 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
4441 /*
4442 * Accept changing compression for a directory
4443 * and set index root accordingly
4444 */
4445 settable |= FILE_ATTR_COMPRESSED;
4446 if ((ni->flags ^ cpu_to_le32(attrib))
4447 & FILE_ATTR_COMPRESSED) {
4448 if (ni->flags & FILE_ATTR_COMPRESSED)
4449 dirflags = const_cpu_to_le16(0);
4450 else
4451 dirflags = ATTR_IS_COMPRESSED;
4452 res = ntfs_attr_set_flags(ni,
4453 AT_INDEX_ROOT,
4454 NTFS_INDEX_I30, 4,
4455 dirflags,
4456 ATTR_COMPRESSION_MASK);
4457 }
4458 }
4459 if (!res) {
4460 ni->flags = (ni->flags & ~settable)
4461 | (cpu_to_le32(attrib) & settable);
4462 NInoFileNameSetDirty(ni);
4463 NInoSetDirty(ni);
4464 }
4465 } else
4466 errno = EEXIST;
4467 } else
4468 errno = EINVAL;
4469 return (res ? -1 : 0);
4470}
4471
4472#endif /* HAVE_SETXATTR */
4473
4474/*
4475 * Open $Secure once for all
4476 * returns zero if it succeeds
4477 * non-zero if it fails. This is not an error (on NTFS v1.x)
4478 */
4479
4480
4481int ntfs_open_secure(ntfs_volume *vol)
4482{
4483 ntfs_inode *ni;
4484 int res;
4485
4486 res = -1;
4487 vol->secure_ni = (ntfs_inode*)NULL;
4488 vol->secure_xsii = (ntfs_index_context*)NULL;
4489 vol->secure_xsdh = (ntfs_index_context*)NULL;
4490 if (vol->major_ver >= 3) {
4491 /* make sure this is a genuine $Secure inode 9 */
4492 ni = ntfs_pathname_to_inode(vol, NULL, "$Secure");
4493 if (ni && (ni->mft_no == 9)) {
4494 vol->secure_reentry = 0;
4495 vol->secure_xsii = ntfs_index_ctx_get(ni,
4496 sii_stream, 4);
4497 vol->secure_xsdh = ntfs_index_ctx_get(ni,
4498 sdh_stream, 4);
4499 if (ni && vol->secure_xsii && vol->secure_xsdh) {
4500 vol->secure_ni = ni;
4501 res = 0;
4502 }
4503 }
4504 }
4505 return (res);
4506}
4507
4508/*
4509 * Final cleaning
4510 * Allocated memory is freed to facilitate the detection of memory leaks
4511 */
4512
4513void ntfs_close_secure(struct SECURITY_CONTEXT *scx)
4514{
4515 ntfs_volume *vol;
4516
4517 vol = scx->vol;
4518 if (vol->secure_ni) {
4519 ntfs_index_ctx_put(vol->secure_xsii);
4520 ntfs_index_ctx_put(vol->secure_xsdh);
4521 ntfs_inode_close(vol->secure_ni);
4522
4523 }
4524 ntfs_free_mapping(scx->mapping);
4525 free_caches(scx);
4526}
4527
4528/*
4529 * API for direct access to security descriptors
4530 * based on Win32 API
4531 */
4532
4533
4534/*
4535 * Selective feeding of a security descriptor into user buffer
4536 *
4537 * Returns TRUE if successful
4538 */
4539
4540static BOOL feedsecurityattr(const char *attr, u32 selection,
4541 char *buf, u32 buflen, u32 *psize)
4542{
4543 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4544 SECURITY_DESCRIPTOR_RELATIVE *pnhead;
4545 const ACL *pdacl;
4546 const ACL *psacl;
4547 const SID *pusid;
4548 const SID *pgsid;
4549 unsigned int offdacl;
4550 unsigned int offsacl;
4551 unsigned int offowner;
4552 unsigned int offgroup;
4553 unsigned int daclsz;
4554 unsigned int saclsz;
4555 unsigned int usidsz;
4556 unsigned int gsidsz;
4557 unsigned int size; /* size of requested attributes */
4558 BOOL ok;
4559 unsigned int pos;
4560 unsigned int avail;
4561 le16 control;
4562
4563 avail = 0;
4564 control = SE_SELF_RELATIVE;
4565 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4566 size = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4567
4568 /* locate DACL if requested and available */
4569 if (phead->dacl && (selection & DACL_SECURITY_INFORMATION)) {
4570 offdacl = le32_to_cpu(phead->dacl);
4571 pdacl = (const ACL*)&attr[offdacl];
4572 daclsz = le16_to_cpu(pdacl->size);
4573 size += daclsz;
4574 avail |= DACL_SECURITY_INFORMATION;
4575 } else
4576 offdacl = daclsz = 0;
4577
4578 /* locate owner if requested and available */
4579 offowner = le32_to_cpu(phead->owner);
4580 if (offowner && (selection & OWNER_SECURITY_INFORMATION)) {
4581 /* find end of USID */
4582 pusid = (const SID*)&attr[offowner];
4583 usidsz = ntfs_sid_size(pusid);
4584 size += usidsz;
4585 avail |= OWNER_SECURITY_INFORMATION;
4586 } else
4587 offowner = usidsz = 0;
4588
4589 /* locate group if requested and available */
4590 offgroup = le32_to_cpu(phead->group);
4591 if (offgroup && (selection & GROUP_SECURITY_INFORMATION)) {
4592 /* find end of GSID */
4593 pgsid = (const SID*)&attr[offgroup];
4594 gsidsz = ntfs_sid_size(pgsid);
4595 size += gsidsz;
4596 avail |= GROUP_SECURITY_INFORMATION;
4597 } else
4598 offgroup = gsidsz = 0;
4599
4600 /* locate SACL if requested and available */
4601 if (phead->sacl && (selection & SACL_SECURITY_INFORMATION)) {
4602 /* find end of SACL */
4603 offsacl = le32_to_cpu(phead->sacl);
4604 psacl = (const ACL*)&attr[offsacl];
4605 saclsz = le16_to_cpu(psacl->size);
4606 size += saclsz;
4607 avail |= SACL_SECURITY_INFORMATION;
4608 } else
4609 offsacl = saclsz = 0;
4610
4611 /*
4612 * Check having enough size in destination buffer
4613 * (required size is returned nevertheless so that
4614 * the request can be reissued with adequate size)
4615 */
4616 if (size > buflen) {
4617 *psize = size;
4618 errno = EINVAL;
4619 ok = FALSE;
4620 } else {
4621 if (selection & OWNER_SECURITY_INFORMATION)
4622 control |= phead->control & SE_OWNER_DEFAULTED;
4623 if (selection & GROUP_SECURITY_INFORMATION)
4624 control |= phead->control & SE_GROUP_DEFAULTED;
4625 if (selection & DACL_SECURITY_INFORMATION)
4626 control |= phead->control
4627 & (SE_DACL_PRESENT
4628 | SE_DACL_DEFAULTED
4629 | SE_DACL_AUTO_INHERITED
4630 | SE_DACL_PROTECTED);
4631 if (selection & SACL_SECURITY_INFORMATION)
4632 control |= phead->control
4633 & (SE_SACL_PRESENT
4634 | SE_SACL_DEFAULTED
4635 | SE_SACL_AUTO_INHERITED
4636 | SE_SACL_PROTECTED);
4637 /*
4638 * copy header and feed new flags, even if no detailed data
4639 */
4640 memcpy(buf,attr,sizeof(SECURITY_DESCRIPTOR_RELATIVE));
4641 pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)buf;
4642 pnhead->control = control;
4643 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4644
4645 /* copy DACL if requested and available */
4646 if (selection & avail & DACL_SECURITY_INFORMATION) {
4647 pnhead->dacl = cpu_to_le32(pos);
4648 memcpy(&buf[pos],&attr[offdacl],daclsz);
4649 pos += daclsz;
4650 } else
4651 pnhead->dacl = const_cpu_to_le32(0);
4652
4653 /* copy SACL if requested and available */
4654 if (selection & avail & SACL_SECURITY_INFORMATION) {
4655 pnhead->sacl = cpu_to_le32(pos);
4656 memcpy(&buf[pos],&attr[offsacl],saclsz);
4657 pos += saclsz;
4658 } else
4659 pnhead->sacl = const_cpu_to_le32(0);
4660
4661 /* copy owner if requested and available */
4662 if (selection & avail & OWNER_SECURITY_INFORMATION) {
4663 pnhead->owner = cpu_to_le32(pos);
4664 memcpy(&buf[pos],&attr[offowner],usidsz);
4665 pos += usidsz;
4666 } else
4667 pnhead->owner = const_cpu_to_le32(0);
4668
4669 /* copy group if requested and available */
4670 if (selection & avail & GROUP_SECURITY_INFORMATION) {
4671 pnhead->group = cpu_to_le32(pos);
4672 memcpy(&buf[pos],&attr[offgroup],gsidsz);
4673 pos += gsidsz;
4674 } else
4675 pnhead->group = const_cpu_to_le32(0);
4676 if (pos != size)
4677 ntfs_log_error("Error in security descriptor size\n");
4678 *psize = size;
4679 ok = TRUE;
4680 }
4681
4682 return (ok);
4683}
4684
4685/*
4686 * Merge a new security descriptor into the old one
4687 * and assign to designated file
4688 *
4689 * Returns TRUE if successful
4690 */
4691
4692static BOOL mergesecurityattr(ntfs_volume *vol, const char *oldattr,
4693 const char *newattr, u32 selection, ntfs_inode *ni)
4694{
4695 const SECURITY_DESCRIPTOR_RELATIVE *oldhead;
4696 const SECURITY_DESCRIPTOR_RELATIVE *newhead;
4697 SECURITY_DESCRIPTOR_RELATIVE *targhead;
4698 const ACL *pdacl;
4699 const ACL *psacl;
4700 const SID *powner;
4701 const SID *pgroup;
4702 int offdacl;
4703 int offsacl;
4704 int offowner;
4705 int offgroup;
4706 unsigned int size;
4707 le16 control;
4708 char *target;
4709 int pos;
4710 int oldattrsz;
4711 int newattrsz;
4712 BOOL ok;
4713
4714 ok = FALSE; /* default return */
4715 oldhead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr;
4716 newhead = (const SECURITY_DESCRIPTOR_RELATIVE*)newattr;
4717 oldattrsz = ntfs_attr_size(oldattr);
4718 newattrsz = ntfs_attr_size(newattr);
4719 target = (char*)ntfs_malloc(oldattrsz + newattrsz);
4720 if (target) {
4721 targhead = (SECURITY_DESCRIPTOR_RELATIVE*)target;
4722 pos = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
4723 control = SE_SELF_RELATIVE;
4724 /*
4725 * copy new DACL if selected
4726 * or keep old DACL if any
4727 */
4728 if ((selection & DACL_SECURITY_INFORMATION) ?
4729 newhead->dacl : oldhead->dacl) {
4730 if (selection & DACL_SECURITY_INFORMATION) {
4731 offdacl = le32_to_cpu(newhead->dacl);
4732 pdacl = (const ACL*)&newattr[offdacl];
4733 } else {
4734 offdacl = le32_to_cpu(oldhead->dacl);
4735 pdacl = (const ACL*)&oldattr[offdacl];
4736 }
4737 size = le16_to_cpu(pdacl->size);
4738 memcpy(&target[pos], pdacl, size);
4739 targhead->dacl = cpu_to_le32(pos);
4740 pos += size;
4741 } else
4742 targhead->dacl = const_cpu_to_le32(0);
4743 if (selection & DACL_SECURITY_INFORMATION) {
4744 control |= newhead->control
4745 & (SE_DACL_PRESENT
4746 | SE_DACL_DEFAULTED
4747 | SE_DACL_PROTECTED);
4748 if (newhead->control & SE_DACL_AUTO_INHERIT_REQ)
4749 control |= SE_DACL_AUTO_INHERITED;
4750 } else
4751 control |= oldhead->control
4752 & (SE_DACL_PRESENT
4753 | SE_DACL_DEFAULTED
4754 | SE_DACL_AUTO_INHERITED
4755 | SE_DACL_PROTECTED);
4756 /*
4757 * copy new SACL if selected
4758 * or keep old SACL if any
4759 */
4760 if ((selection & SACL_SECURITY_INFORMATION) ?
4761 newhead->sacl : oldhead->sacl) {
4762 if (selection & SACL_SECURITY_INFORMATION) {
4763 offsacl = le32_to_cpu(newhead->sacl);
4764 psacl = (const ACL*)&newattr[offsacl];
4765 } else {
4766 offsacl = le32_to_cpu(oldhead->sacl);
4767 psacl = (const ACL*)&oldattr[offsacl];
4768 }
4769 size = le16_to_cpu(psacl->size);
4770 memcpy(&target[pos], psacl, size);
4771 targhead->sacl = cpu_to_le32(pos);
4772 pos += size;
4773 } else
4774 targhead->sacl = const_cpu_to_le32(0);
4775 if (selection & SACL_SECURITY_INFORMATION) {
4776 control |= newhead->control
4777 & (SE_SACL_PRESENT
4778 | SE_SACL_DEFAULTED
4779 | SE_SACL_PROTECTED);
4780 if (newhead->control & SE_SACL_AUTO_INHERIT_REQ)
4781 control |= SE_SACL_AUTO_INHERITED;
4782 } else
4783 control |= oldhead->control
4784 & (SE_SACL_PRESENT
4785 | SE_SACL_DEFAULTED
4786 | SE_SACL_AUTO_INHERITED
4787 | SE_SACL_PROTECTED);
4788 /*
4789 * copy new OWNER if selected
4790 * or keep old OWNER if any
4791 */
4792 if ((selection & OWNER_SECURITY_INFORMATION) ?
4793 newhead->owner : oldhead->owner) {
4794 if (selection & OWNER_SECURITY_INFORMATION) {
4795 offowner = le32_to_cpu(newhead->owner);
4796 powner = (const SID*)&newattr[offowner];
4797 } else {
4798 offowner = le32_to_cpu(oldhead->owner);
4799 powner = (const SID*)&oldattr[offowner];
4800 }
4801 size = ntfs_sid_size(powner);
4802 memcpy(&target[pos], powner, size);
4803 targhead->owner = cpu_to_le32(pos);
4804 pos += size;
4805 } else
4806 targhead->owner = const_cpu_to_le32(0);
4807 if (selection & OWNER_SECURITY_INFORMATION)
4808 control |= newhead->control & SE_OWNER_DEFAULTED;
4809 else
4810 control |= oldhead->control & SE_OWNER_DEFAULTED;
4811 /*
4812 * copy new GROUP if selected
4813 * or keep old GROUP if any
4814 */
4815 if ((selection & GROUP_SECURITY_INFORMATION) ?
4816 newhead->group : oldhead->group) {
4817 if (selection & GROUP_SECURITY_INFORMATION) {
4818 offgroup = le32_to_cpu(newhead->group);
4819 pgroup = (const SID*)&newattr[offgroup];
4820 control |= newhead->control
4821 & SE_GROUP_DEFAULTED;
4822 } else {
4823 offgroup = le32_to_cpu(oldhead->group);
4824 pgroup = (const SID*)&oldattr[offgroup];
4825 control |= oldhead->control
4826 & SE_GROUP_DEFAULTED;
4827 }
4828 size = ntfs_sid_size(pgroup);
4829 memcpy(&target[pos], pgroup, size);
4830 targhead->group = cpu_to_le32(pos);
4831 pos += size;
4832 } else
4833 targhead->group = const_cpu_to_le32(0);
4834 if (selection & GROUP_SECURITY_INFORMATION)
4835 control |= newhead->control & SE_GROUP_DEFAULTED;
4836 else
4837 control |= oldhead->control & SE_GROUP_DEFAULTED;
4838 targhead->revision = SECURITY_DESCRIPTOR_REVISION;
4839 targhead->alignment = 0;
4840 targhead->control = control;
4841 ok = !update_secur_descr(vol, target, ni);
4842 free(target);
4843 }
4844 return (ok);
4845}
4846
4847/*
4848 * Return the security descriptor of a file
4849 * This is intended to be similar to GetFileSecurity() from Win32
4850 * in order to facilitate the development of portable tools
4851 *
4852 * returns zero if unsuccessful (following Win32 conventions)
4853 * -1 if no securid
4854 * the securid if any
4855 *
4856 * The Win32 API is :
4857 *
4858 * BOOL WINAPI GetFileSecurity(
4859 * __in LPCTSTR lpFileName,
4860 * __in SECURITY_INFORMATION RequestedInformation,
4861 * __out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor,
4862 * __in DWORD nLength,
4863 * __out LPDWORD lpnLengthNeeded
4864 * );
4865 *
4866 */
4867
4868int ntfs_get_file_security(struct SECURITY_API *scapi,
4869 const char *path, u32 selection,
4870 char *buf, u32 buflen, u32 *psize)
4871{
4872 ntfs_inode *ni;
4873 char *attr;
4874 int res;
4875
4876 res = 0; /* default return */
4877 if (scapi && (scapi->magic == MAGIC_API)) {
4878 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4879 if (ni) {
4880 attr = getsecurityattr(scapi->security.vol, ni);
4881 if (attr) {
4882 if (feedsecurityattr(attr,selection,
4883 buf,buflen,psize)) {
4884 if (test_nino_flag(ni, v3_Extensions)
4885 && ni->security_id)
4886 res = le32_to_cpu(
4887 ni->security_id);
4888 else
4889 res = -1;
4890 }
4891 free(attr);
4892 }
4893 ntfs_inode_close(ni);
4894 } else
4895 errno = ENOENT;
4896 if (!res) *psize = 0;
4897 } else
4898 errno = EINVAL; /* do not clear *psize */
4899 return (res);
4900}
4901
4902
4903/*
4904 * Set the security descriptor of a file or directory
4905 * This is intended to be similar to SetFileSecurity() from Win32
4906 * in order to facilitate the development of portable tools
4907 *
4908 * returns zero if unsuccessful (following Win32 conventions)
4909 * -1 if no securid
4910 * the securid if any
4911 *
4912 * The Win32 API is :
4913 *
4914 * BOOL WINAPI SetFileSecurity(
4915 * __in LPCTSTR lpFileName,
4916 * __in SECURITY_INFORMATION SecurityInformation,
4917 * __in PSECURITY_DESCRIPTOR pSecurityDescriptor
4918 * );
4919 */
4920
4921int ntfs_set_file_security(struct SECURITY_API *scapi,
4922 const char *path, u32 selection, const char *attr)
4923{
4924 const SECURITY_DESCRIPTOR_RELATIVE *phead;
4925 ntfs_inode *ni;
4926 int attrsz;
4927 BOOL missing;
4928 char *oldattr;
4929 int res;
4930
4931 res = 0; /* default return */
4932 if (scapi && (scapi->magic == MAGIC_API) && attr) {
4933 phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
4934 attrsz = ntfs_attr_size(attr);
4935 /* if selected, owner and group must be present or defaulted */
4936 missing = ((selection & OWNER_SECURITY_INFORMATION)
4937 && !phead->owner
4938 && !(phead->control & SE_OWNER_DEFAULTED))
4939 || ((selection & GROUP_SECURITY_INFORMATION)
4940 && !phead->group
4941 && !(phead->control & SE_GROUP_DEFAULTED));
4942 if (!missing
4943 && (phead->control & SE_SELF_RELATIVE)
4944 && ntfs_valid_descr(attr, attrsz)) {
4945 ni = ntfs_pathname_to_inode(scapi->security.vol,
4946 NULL, path);
4947 if (ni) {
4948 oldattr = getsecurityattr(scapi->security.vol,
4949 ni);
4950 if (oldattr) {
4951 if (mergesecurityattr(
4952 scapi->security.vol,
4953 oldattr, attr,
4954 selection, ni)) {
4955 if (test_nino_flag(ni,
4956 v3_Extensions))
4957 res = le32_to_cpu(
4958 ni->security_id);
4959 else
4960 res = -1;
4961 }
4962 free(oldattr);
4963 }
4964 ntfs_inode_close(ni);
4965 }
4966 } else
4967 errno = EINVAL;
4968 } else
4969 errno = EINVAL;
4970 return (res);
4971}
4972
4973
4974/*
4975 * Return the attributes of a file
4976 * This is intended to be similar to GetFileAttributes() from Win32
4977 * in order to facilitate the development of portable tools
4978 *
4979 * returns -1 if unsuccessful (Win32 : INVALID_FILE_ATTRIBUTES)
4980 *
4981 * The Win32 API is :
4982 *
4983 * DWORD WINAPI GetFileAttributes(
4984 * __in LPCTSTR lpFileName
4985 * );
4986 */
4987
4988int ntfs_get_file_attributes(struct SECURITY_API *scapi, const char *path)
4989{
4990 ntfs_inode *ni;
4991 s32 attrib;
4992
4993 attrib = -1; /* default return */
4994 if (scapi && (scapi->magic == MAGIC_API) && path) {
4995 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
4996 if (ni) {
4997 attrib = le32_to_cpu(ni->flags);
4998 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
4999 attrib |= const_le32_to_cpu(FILE_ATTR_DIRECTORY);
5000 else
5001 attrib &= ~const_le32_to_cpu(FILE_ATTR_DIRECTORY);
5002 if (!attrib)
5003 attrib |= const_le32_to_cpu(FILE_ATTR_NORMAL);
5004
5005 ntfs_inode_close(ni);
5006 } else
5007 errno = ENOENT;
5008 } else
5009 errno = EINVAL; /* do not clear *psize */
5010 return (attrib);
5011}
5012
5013
5014/*
5015 * Set attributes to a file or directory
5016 * This is intended to be similar to SetFileAttributes() from Win32
5017 * in order to facilitate the development of portable tools
5018 *
5019 * Only a few flags can be set (same list as Win32)
5020 *
5021 * returns zero if unsuccessful (following Win32 conventions)
5022 * nonzero if successful
5023 *
5024 * The Win32 API is :
5025 *
5026 * BOOL WINAPI SetFileAttributes(
5027 * __in LPCTSTR lpFileName,
5028 * __in DWORD dwFileAttributes
5029 * );
5030 */
5031
5032BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
5033 const char *path, s32 attrib)
5034{
5035 ntfs_inode *ni;
5036 le32 settable;
5037 ATTR_FLAGS dirflags;
5038 int res;
5039
5040 res = 0; /* default return */
5041 if (scapi && (scapi->magic == MAGIC_API) && path) {
5042 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
5043 if (ni) {
5044 settable = FILE_ATTR_SETTABLE;
5045 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
5046 /*
5047 * Accept changing compression for a directory
5048 * and set index root accordingly
5049 */
5050 settable |= FILE_ATTR_COMPRESSED;
5051 if ((ni->flags ^ cpu_to_le32(attrib))
5052 & FILE_ATTR_COMPRESSED) {
5053 if (ni->flags & FILE_ATTR_COMPRESSED)
5054 dirflags = const_cpu_to_le16(0);
5055 else
5056 dirflags = ATTR_IS_COMPRESSED;
5057 res = ntfs_attr_set_flags(ni,
5058 AT_INDEX_ROOT,
5059 NTFS_INDEX_I30, 4,
5060 dirflags,
5061 ATTR_COMPRESSION_MASK);
5062 }
5063 }
5064 if (!res) {
5065 ni->flags = (ni->flags & ~settable)
5066 | (cpu_to_le32(attrib) & settable);
5067 NInoSetDirty(ni);
5068 NInoFileNameSetDirty(ni);
5069 }
5070 if (!ntfs_inode_close(ni))
5071 res = -1;
5072 } else
5073 errno = ENOENT;
5074 }
5075 return (res);
5076}
5077
5078
5079BOOL ntfs_read_directory(struct SECURITY_API *scapi,
5080 const char *path, ntfs_filldir_t callback, void *context)
5081{
5082 ntfs_inode *ni;
5083 BOOL ok;
5084 s64 pos;
5085
5086 ok = FALSE; /* default return */
5087 if (scapi && (scapi->magic == MAGIC_API) && callback) {
5088 ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
5089 if (ni) {
5090 if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
5091 pos = 0;
5092 ntfs_readdir(ni,&pos,context,callback);
5093 ok = !ntfs_inode_close(ni);
5094 } else {
5095 ntfs_inode_close(ni);
5096 errno = ENOTDIR;
5097 }
5098 } else
5099 errno = ENOENT;
5100 } else
5101 errno = EINVAL; /* do not clear *psize */
5102 return (ok);
5103}
5104
5105/*
5106 * read $SDS (for auditing security data)
5107 *
5108 * Returns the number or read bytes, or -1 if there is an error
5109 */
5110
5111int ntfs_read_sds(struct SECURITY_API *scapi,
5112 char *buf, u32 size, u32 offset)
5113{
5114 int got;
5115
5116 got = -1; /* default return */
5117 if (scapi && (scapi->magic == MAGIC_API)) {
5118 if (scapi->security.vol->secure_ni)
5119 got = ntfs_attr_data_read(scapi->security.vol->secure_ni,
5120 STREAM_SDS, 4, buf, size, offset);
5121 else
5122 errno = EOPNOTSUPP;
5123 } else
5124 errno = EINVAL;
5125 return (got);
5126}
5127
5128/*
5129 * read $SII (for auditing security data)
5130 *
5131 * Returns next entry, or NULL if there is an error
5132 */
5133
5134INDEX_ENTRY *ntfs_read_sii(struct SECURITY_API *scapi,
5135 INDEX_ENTRY *entry)
5136{
5137 SII_INDEX_KEY key;
5138 INDEX_ENTRY *ret;
5139 BOOL found;
5140 ntfs_index_context *xsii;
5141
5142 ret = (INDEX_ENTRY*)NULL; /* default return */
5143 if (scapi && (scapi->magic == MAGIC_API)) {
5144 xsii = scapi->security.vol->secure_xsii;
5145 if (xsii) {
5146 if (!entry) {
5147 key.security_id = const_cpu_to_le32(0);
5148 found = !ntfs_index_lookup((char*)&key,
5149 sizeof(SII_INDEX_KEY), xsii);
5150 /* not supposed to find */
5151 if (!found && (errno == ENOENT))
5152 ret = xsii->entry;
5153 } else
5154 ret = ntfs_index_next(entry,xsii);
5155 if (!ret)
5156 errno = ENODATA;
5157 } else
5158 errno = EOPNOTSUPP;
5159 } else
5160 errno = EINVAL;
5161 return (ret);
5162}
5163
5164/*
5165 * read $SDH (for auditing security data)
5166 *
5167 * Returns next entry, or NULL if there is an error
5168 */
5169
5170INDEX_ENTRY *ntfs_read_sdh(struct SECURITY_API *scapi,
5171 INDEX_ENTRY *entry)
5172{
5173 SDH_INDEX_KEY key;
5174 INDEX_ENTRY *ret;
5175 BOOL found;
5176 ntfs_index_context *xsdh;
5177
5178 ret = (INDEX_ENTRY*)NULL; /* default return */
5179 if (scapi && (scapi->magic == MAGIC_API)) {
5180 xsdh = scapi->security.vol->secure_xsdh;
5181 if (xsdh) {
5182 if (!entry) {
5183 key.hash = const_cpu_to_le32(0);
5184 key.security_id = const_cpu_to_le32(0);
5185 found = !ntfs_index_lookup((char*)&key,
5186 sizeof(SDH_INDEX_KEY), xsdh);
5187 /* not supposed to find */
5188 if (!found && (errno == ENOENT))
5189 ret = xsdh->entry;
5190 } else
5191 ret = ntfs_index_next(entry,xsdh);
5192 if (!ret)
5193 errno = ENODATA;
5194 } else errno = ENOTSUP;
5195 } else
5196 errno = EINVAL;
5197 return (ret);
5198}
5199
5200/*
5201 * Get the mapped user SID
5202 * A buffer of 40 bytes has to be supplied
5203 *
5204 * returns the size of the SID, or zero and errno set if not found
5205 */
5206
5207int ntfs_get_usid(struct SECURITY_API *scapi, uid_t uid, char *buf)
5208{
5209 const SID *usid;
5210 BIGSID defusid;
5211 int size;
5212
5213 size = 0;
5214 if (scapi && (scapi->magic == MAGIC_API)) {
5215 usid = ntfs_find_usid(scapi->security.mapping[MAPUSERS], uid, (SID*)&defusid);
5216 if (usid) {
5217 size = ntfs_sid_size(usid);
5218 memcpy(buf,usid,size);
5219 } else
5220 errno = ENODATA;
5221 } else
5222 errno = EINVAL;
5223 return (size);
5224}
5225
5226/*
5227 * Get the mapped group SID
5228 * A buffer of 40 bytes has to be supplied
5229 *
5230 * returns the size of the SID, or zero and errno set if not found
5231 */
5232
5233int ntfs_get_gsid(struct SECURITY_API *scapi, gid_t gid, char *buf)
5234{
5235 const SID *gsid;
5236 BIGSID defgsid;
5237 int size;
5238
5239 size = 0;
5240 if (scapi && (scapi->magic == MAGIC_API)) {
5241 gsid = ntfs_find_gsid(scapi->security.mapping[MAPGROUPS], gid, (SID*)&defgsid);
5242 if (gsid) {
5243 size = ntfs_sid_size(gsid);
5244 memcpy(buf,gsid,size);
5245 } else
5246 errno = ENODATA;
5247 } else
5248 errno = EINVAL;
5249 return (size);
5250}
5251
5252/*
5253 * Get the user mapped to a SID
5254 *
5255 * returns the uid, or -1 if not found
5256 */
5257
5258int ntfs_get_user(struct SECURITY_API *scapi, const SID *usid)
5259{
5260 int uid;
5261
5262 uid = -1;
5263 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(usid)) {
5264 if (ntfs_same_sid(usid,adminsid))
5265 uid = 0;
5266 else {
5267 uid = ntfs_find_user(scapi->security.mapping[MAPUSERS], usid);
5268 if (!uid) {
5269 uid = -1;
5270 errno = ENODATA;
5271 }
5272 }
5273 } else
5274 errno = EINVAL;
5275 return (uid);
5276}
5277
5278/*
5279 * Get the group mapped to a SID
5280 *
5281 * returns the uid, or -1 if not found
5282 */
5283
5284int ntfs_get_group(struct SECURITY_API *scapi, const SID *gsid)
5285{
5286 int gid;
5287
5288 gid = -1;
5289 if (scapi && (scapi->magic == MAGIC_API) && ntfs_valid_sid(gsid)) {
5290 if (ntfs_same_sid(gsid,adminsid))
5291 gid = 0;
5292 else {
5293 gid = ntfs_find_group(scapi->security.mapping[MAPGROUPS], gsid);
5294 if (!gid) {
5295 gid = -1;
5296 errno = ENODATA;
5297 }
5298 }
5299 } else
5300 errno = EINVAL;
5301 return (gid);
5302}
5303
5304/*
5305 * Initializations before calling ntfs_get_file_security()
5306 * ntfs_set_file_security() and ntfs_read_directory()
5307 *
5308 * Only allowed for root
5309 *
5310 * Returns an (obscured) struct SECURITY_API* needed for further calls
5311 * NULL if not root (EPERM) or device is mounted (EBUSY)
5312 */
5313
5314struct SECURITY_API *ntfs_initialize_file_security(const char *device,
5315 unsigned long flags)
5316{
5317 ntfs_volume *vol;
5318 unsigned long mntflag;
5319 int mnt;
5320 struct SECURITY_API *scapi;
5321 struct SECURITY_CONTEXT *scx;
5322
5323 scapi = (struct SECURITY_API*)NULL;
5324 mnt = ntfs_check_if_mounted(device, &mntflag);
5325 if (!mnt && !(mntflag & NTFS_MF_MOUNTED) && !getuid()) {
5326 vol = ntfs_mount(device, flags);
5327 if (vol) {
5328 scapi = (struct SECURITY_API*)
5329 ntfs_malloc(sizeof(struct SECURITY_API));
5330 if (!ntfs_volume_get_free_space(vol)
5331 && scapi) {
5332 scapi->magic = MAGIC_API;
5333 scapi->seccache = (struct PERMISSIONS_CACHE*)NULL;
5334 scx = &scapi->security;
5335 scx->vol = vol;
5336 scx->uid = getuid();
5337 scx->gid = getgid();
5338 scx->pseccache = &scapi->seccache;
5339 scx->vol->secure_flags = 0;
5340 /* accept no mapping and no $Secure */
5341 ntfs_build_mapping(scx,(const char*)NULL,TRUE);
5342 ntfs_open_secure(vol);
5343 } else {
5344 if (scapi)
5345 free(scapi);
5346 else
5347 errno = ENOMEM;
5348 mnt = ntfs_umount(vol,FALSE);
5349 scapi = (struct SECURITY_API*)NULL;
5350 }
5351 }
5352 } else
5353 if (getuid())
5354 errno = EPERM;
5355 else
5356 errno = EBUSY;
5357 return (scapi);
5358}
5359
5360/*
5361 * Leaving after ntfs_initialize_file_security()
5362 *
5363 * Returns FALSE if FAILED
5364 */
5365
5366BOOL ntfs_leave_file_security(struct SECURITY_API *scapi)
5367{
5368 int ok;
5369 ntfs_volume *vol;
5370
5371 ok = FALSE;
5372 if (scapi && (scapi->magic == MAGIC_API) && scapi->security.vol) {
5373 vol = scapi->security.vol;
5374 ntfs_close_secure(&scapi->security);
5375 free(scapi);
5376 if (!ntfs_umount(vol, 0))
5377 ok = TRUE;
5378 }
5379 return (ok);
5380}
5381