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