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