1 /*
2    Unix SMB/CIFS implementation.
3 
4    POSIX NTVFS backend - xattr support
5 
6    Copyright (C) Andrew Tridgell 2004
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 
23 #include "includes.h"
24 #include "vfs_posix.h"
25 #include "lib/util/unix_privs.h"
26 #include "librpc/gen_ndr/ndr_xattr.h"
27 
28 /*
29   pull a xattr as a blob
30 */
pull_xattr_blob(struct pvfs_state * pvfs,TALLOC_CTX * mem_ctx,const char * attr_name,const char * fname,int fd,size_t estimated_size,DATA_BLOB * blob)31 static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs,
32 				TALLOC_CTX *mem_ctx,
33 				const char *attr_name,
34 				const char *fname,
35 				int fd,
36 				size_t estimated_size,
37 				DATA_BLOB *blob)
38 {
39 	NTSTATUS status;
40 
41 	if (pvfs->ea_db) {
42 		return pull_xattr_blob_tdb(pvfs, mem_ctx, attr_name, fname,
43 					   fd, estimated_size, blob);
44 	}
45 
46 	status = pull_xattr_blob_system(pvfs, mem_ctx, attr_name, fname,
47 					fd, estimated_size, blob);
48 
49 	/* if the filesystem doesn't support them, then tell pvfs not to try again */
50 	if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)||
51 	    NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)||
52 	    NT_STATUS_EQUAL(status, NT_STATUS_INVALID_SYSTEM_SERVICE)) {
53 		DEBUG(5,("pvfs_xattr: xattr not supported in filesystem: %s\n", nt_errstr(status)));
54 		pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
55 		status = NT_STATUS_NOT_FOUND;
56 	}
57 
58 	return status;
59 }
60 
61 /*
62   push a xattr as a blob
63 */
push_xattr_blob(struct pvfs_state * pvfs,const char * attr_name,const char * fname,int fd,const DATA_BLOB * blob)64 static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs,
65 				const char *attr_name,
66 				const char *fname,
67 				int fd,
68 				const DATA_BLOB *blob)
69 {
70 	if (pvfs->ea_db) {
71 		return push_xattr_blob_tdb(pvfs, attr_name, fname, fd, blob);
72 	}
73 	return push_xattr_blob_system(pvfs, attr_name, fname, fd, blob);
74 }
75 
76 
77 /*
78   delete a xattr
79 */
delete_xattr(struct pvfs_state * pvfs,const char * attr_name,const char * fname,int fd)80 static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name,
81 			     const char *fname, int fd)
82 {
83 	if (pvfs->ea_db) {
84 		return delete_xattr_tdb(pvfs, attr_name, fname, fd);
85 	}
86 	return delete_xattr_system(pvfs, attr_name, fname, fd);
87 }
88 
89 /*
90   a hook called on unlink - allows the tdb xattr backend to cleanup
91 */
pvfs_xattr_unlink_hook(struct pvfs_state * pvfs,const char * fname)92 NTSTATUS pvfs_xattr_unlink_hook(struct pvfs_state *pvfs, const char *fname)
93 {
94 	if (pvfs->ea_db) {
95 		return unlink_xattr_tdb(pvfs, fname);
96 	}
97 	return unlink_xattr_system(pvfs, fname);
98 }
99 
100 
101 /*
102   load a NDR structure from a xattr
103 */
pvfs_xattr_ndr_load(struct pvfs_state * pvfs,TALLOC_CTX * mem_ctx,const char * fname,int fd,const char * attr_name,void * p,void * pull_fn)104 _PUBLIC_ NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs,
105 			     TALLOC_CTX *mem_ctx,
106 			     const char *fname, int fd, const char *attr_name,
107 			     void *p, void *pull_fn)
108 {
109 	NTSTATUS status;
110 	DATA_BLOB blob;
111 
112 	status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname,
113 				 fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob);
114 	if (!NT_STATUS_IS_OK(status)) {
115 		return status;
116 	}
117 
118 	/* pull the blob */
119 	status = ndr_pull_struct_blob(&blob, mem_ctx, p, (ndr_pull_flags_fn_t)pull_fn);
120 
121 	data_blob_free(&blob);
122 
123 	return status;
124 }
125 
126 /*
127   save a NDR structure into a xattr
128 */
pvfs_xattr_ndr_save(struct pvfs_state * pvfs,const char * fname,int fd,const char * attr_name,void * p,void * push_fn)129 _PUBLIC_ NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs,
130 			     const char *fname, int fd, const char *attr_name,
131 			     void *p, void *push_fn)
132 {
133 	TALLOC_CTX *mem_ctx = talloc_new(NULL);
134 	DATA_BLOB blob;
135 	NTSTATUS status;
136 
137 	status = ndr_push_struct_blob(&blob, mem_ctx, p, (ndr_push_flags_fn_t)push_fn);
138 	if (!NT_STATUS_IS_OK(status)) {
139 		talloc_free(mem_ctx);
140 		return status;
141 	}
142 
143 	status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob);
144 	talloc_free(mem_ctx);
145 
146 	return status;
147 }
148 
149 
150 /*
151   fill in file attributes from extended attributes
152 */
pvfs_dosattrib_load(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd)153 NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
154 {
155 	NTSTATUS status;
156 	struct xattr_DosAttrib attrib;
157 	TALLOC_CTX *mem_ctx = talloc_new(name);
158 	struct xattr_DosInfo1 *info1;
159 	struct xattr_DosInfo2 *info2;
160 
161 	if (name->stream_name != NULL) {
162 		name->stream_exists = False;
163 	} else {
164 		name->stream_exists = True;
165 	}
166 
167 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
168 		return NT_STATUS_OK;
169 	}
170 
171 	status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name,
172 				     fd, XATTR_DOSATTRIB_NAME,
173 				     &attrib,
174 				     (ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib);
175 
176 	/* not having a DosAttrib is not an error */
177 	if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
178 		talloc_free(mem_ctx);
179 		return pvfs_stream_info(pvfs, name, fd);
180 	}
181 
182 	if (!NT_STATUS_IS_OK(status)) {
183 		talloc_free(mem_ctx);
184 		return status;
185 	}
186 
187 	switch (attrib.version) {
188 	case 1:
189 		info1 = &attrib.info.info1;
190 		name->dos.attrib = pvfs_attrib_normalise(info1->attrib,
191 							 name->st.st_mode);
192 		name->dos.ea_size = info1->ea_size;
193 		if (name->st.st_size == info1->size) {
194 			name->dos.alloc_size =
195 				pvfs_round_alloc_size(pvfs, info1->alloc_size);
196 		}
197 		if (!null_nttime(info1->create_time)) {
198 			name->dos.create_time = info1->create_time;
199 		}
200 		if (!null_nttime(info1->change_time)) {
201 			name->dos.change_time = info1->change_time;
202 		}
203 		name->dos.flags = 0;
204 		break;
205 
206 	case 2:
207 		info2 = &attrib.info.info2;
208 		name->dos.attrib = pvfs_attrib_normalise(info2->attrib,
209 							 name->st.st_mode);
210 		name->dos.ea_size = info2->ea_size;
211 		if (name->st.st_size == info2->size) {
212 			name->dos.alloc_size =
213 				pvfs_round_alloc_size(pvfs, info2->alloc_size);
214 		}
215 		if (!null_nttime(info2->create_time)) {
216 			name->dos.create_time = info2->create_time;
217 		}
218 		if (!null_nttime(info2->change_time)) {
219 			name->dos.change_time = info2->change_time;
220 		}
221 		name->dos.flags = info2->flags;
222 		if (name->dos.flags & XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME) {
223 			name->dos.write_time = info2->write_time;
224 		}
225 		break;
226 
227 	default:
228 		DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
229 			 attrib.version, name->full_name));
230 		talloc_free(mem_ctx);
231 		return NT_STATUS_INVALID_LEVEL;
232 	}
233 	talloc_free(mem_ctx);
234 
235 	status = pvfs_stream_info(pvfs, name, fd);
236 
237 	return status;
238 }
239 
240 
241 /*
242   save the file attribute into the xattr
243 */
pvfs_dosattrib_save(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd)244 NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
245 {
246 	struct xattr_DosAttrib attrib;
247 	struct xattr_DosInfo2 *info2;
248 
249 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
250 		return NT_STATUS_OK;
251 	}
252 
253 	attrib.version = 2;
254 	info2 = &attrib.info.info2;
255 
256 	name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib, name->st.st_mode);
257 
258 	info2->attrib      = name->dos.attrib;
259 	info2->ea_size     = name->dos.ea_size;
260 	info2->size        = name->st.st_size;
261 	info2->alloc_size  = name->dos.alloc_size;
262 	info2->create_time = name->dos.create_time;
263 	info2->change_time = name->dos.change_time;
264 	info2->write_time  = name->dos.write_time;
265 	info2->flags       = name->dos.flags;
266 	info2->name        = "";
267 
268 	return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
269 				   XATTR_DOSATTRIB_NAME, &attrib,
270 				   (ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib);
271 }
272 
273 
274 /*
275   load the set of DOS EAs
276 */
pvfs_doseas_load(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd,struct xattr_DosEAs * eas)277 NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
278 			  struct xattr_DosEAs *eas)
279 {
280 	NTSTATUS status;
281 	ZERO_STRUCTP(eas);
282 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
283 		return NT_STATUS_OK;
284 	}
285 	status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
286 				     eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs);
287 	if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
288 		return NT_STATUS_OK;
289 	}
290 	return status;
291 }
292 
293 /*
294   save the set of DOS EAs
295 */
pvfs_doseas_save(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd,struct xattr_DosEAs * eas)296 NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
297 			  struct xattr_DosEAs *eas)
298 {
299 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
300 		return NT_STATUS_OK;
301 	}
302 	return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas,
303 				   (ndr_push_flags_fn_t)ndr_push_xattr_DosEAs);
304 }
305 
306 
307 /*
308   load the set of streams from extended attributes
309 */
pvfs_streams_load(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd,struct xattr_DosStreams * streams)310 NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
311 			   struct xattr_DosStreams *streams)
312 {
313 	NTSTATUS status;
314 	ZERO_STRUCTP(streams);
315 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
316 		return NT_STATUS_OK;
317 	}
318 	status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd,
319 				     XATTR_DOSSTREAMS_NAME,
320 				     streams,
321 				     (ndr_pull_flags_fn_t)ndr_pull_xattr_DosStreams);
322 	if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
323 		return NT_STATUS_OK;
324 	}
325 	return status;
326 }
327 
328 /*
329   save the set of streams into filesystem xattr
330 */
pvfs_streams_save(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd,struct xattr_DosStreams * streams)331 NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
332 			   struct xattr_DosStreams *streams)
333 {
334 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
335 		return NT_STATUS_OK;
336 	}
337 	return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
338 				   XATTR_DOSSTREAMS_NAME,
339 				   streams,
340 				   (ndr_push_flags_fn_t)ndr_push_xattr_DosStreams);
341 }
342 
343 
344 /*
345   load the current ACL from extended attributes
346 */
pvfs_acl_load(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd,struct xattr_NTACL * acl)347 NTSTATUS pvfs_acl_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
348 		       struct xattr_NTACL *acl)
349 {
350 	NTSTATUS status;
351 	ZERO_STRUCTP(acl);
352 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
353 		return NT_STATUS_NOT_FOUND;
354 	}
355 	status = pvfs_xattr_ndr_load(pvfs, acl, name->full_name, fd,
356 				     XATTR_NTACL_NAME,
357 				     acl,
358 				     (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
359 	return status;
360 }
361 
362 /*
363   save the acl for a file into filesystem xattr
364 */
pvfs_acl_save(struct pvfs_state * pvfs,struct pvfs_filename * name,int fd,struct xattr_NTACL * acl)365 NTSTATUS pvfs_acl_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
366 		       struct xattr_NTACL *acl)
367 {
368 	NTSTATUS status;
369 	void *privs;
370 
371 	if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
372 		return NT_STATUS_OK;
373 	}
374 
375 	/* this xattr is in the "system" namespace, so we need
376 	   admin privileges to set it */
377 	privs = root_privileges();
378 	status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
379 				     XATTR_NTACL_NAME,
380 				     acl,
381 				     (ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
382 	talloc_free(privs);
383 	return status;
384 }
385 
386 /*
387   create a zero length xattr with the given name
388 */
pvfs_xattr_create(struct pvfs_state * pvfs,const char * fname,int fd,const char * attr_prefix,const char * attr_name)389 NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs,
390 			   const char *fname, int fd,
391 			   const char *attr_prefix,
392 			   const char *attr_name)
393 {
394 	NTSTATUS status;
395 	DATA_BLOB blob = data_blob(NULL, 0);
396 	char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
397 	if (aname == NULL) {
398 		return NT_STATUS_NO_MEMORY;
399 	}
400 	status = push_xattr_blob(pvfs, aname, fname, fd, &blob);
401 	talloc_free(aname);
402 	return status;
403 }
404 
405 
406 /*
407   delete a xattr with the given name
408 */
pvfs_xattr_delete(struct pvfs_state * pvfs,const char * fname,int fd,const char * attr_prefix,const char * attr_name)409 NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs,
410 			   const char *fname, int fd,
411 			   const char *attr_prefix,
412 			   const char *attr_name)
413 {
414 	NTSTATUS status;
415 	char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
416 	if (aname == NULL) {
417 		return NT_STATUS_NO_MEMORY;
418 	}
419 	status = delete_xattr(pvfs, aname, fname, fd);
420 	talloc_free(aname);
421 	return status;
422 }
423 
424 /*
425   load a xattr with the given name
426 */
pvfs_xattr_load(struct pvfs_state * pvfs,TALLOC_CTX * mem_ctx,const char * fname,int fd,const char * attr_prefix,const char * attr_name,size_t estimated_size,DATA_BLOB * blob)427 NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs,
428 			 TALLOC_CTX *mem_ctx,
429 			 const char *fname, int fd,
430 			 const char *attr_prefix,
431 			 const char *attr_name,
432 			 size_t estimated_size,
433 			 DATA_BLOB *blob)
434 {
435 	NTSTATUS status;
436 	char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name);
437 	if (aname == NULL) {
438 		return NT_STATUS_NO_MEMORY;
439 	}
440 	status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob);
441 	talloc_free(aname);
442 	return status;
443 }
444 
445 /*
446   save a xattr with the given name
447 */
pvfs_xattr_save(struct pvfs_state * pvfs,const char * fname,int fd,const char * attr_prefix,const char * attr_name,const DATA_BLOB * blob)448 NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs,
449 			 const char *fname, int fd,
450 			 const char *attr_prefix,
451 			 const char *attr_name,
452 			 const DATA_BLOB *blob)
453 {
454 	NTSTATUS status;
455 	char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
456 	if (aname == NULL) {
457 		return NT_STATUS_NO_MEMORY;
458 	}
459 	status = push_xattr_blob(pvfs, aname, fname, fd, blob);
460 	talloc_free(aname);
461 	return status;
462 }
463 
464 
465 /*
466   probe for system support for xattrs
467 */
pvfs_xattr_probe(struct pvfs_state * pvfs)468 void pvfs_xattr_probe(struct pvfs_state *pvfs)
469 {
470 	TALLOC_CTX *tmp_ctx = talloc_new(pvfs);
471 	DATA_BLOB blob;
472 	pull_xattr_blob(pvfs, tmp_ctx, "user.XattrProbe", pvfs->base_directory,
473 			-1, 1, &blob);
474 	pull_xattr_blob(pvfs, tmp_ctx, "security.XattrProbe", pvfs->base_directory,
475 			-1, 1, &blob);
476 	talloc_free(tmp_ctx);
477 }
478