2 * Unix SMB/CIFS implementation.
3 * Group Policy Object Support
4 * Copyright (C) Wilco Baan Hofman 2008-2010
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "system/dir.h"
21 #include "system/filesys.h"
22 #include "lib/policy/policy.h"
23 #include "libcli/raw/smb.h"
24 #include "libcli/libcli.h"
25 #include "param/param.h"
26 #include "libcli/resolve/resolve.h"
27 #include "libcli/raw/libcliraw.h"
31 #define GP_MAX_DEPTH 25
33 struct gp_file_entry
{
39 struct gp_file_entry
*files
;
41 struct gp_list_state
{
42 struct smbcli_tree
*tree
;
44 const char *cur_rel_path
;
45 const char *share_path
;
47 struct gp_file_list list
;
50 static NTSTATUS
gp_do_list(const char *, struct gp_list_state
*);
52 /* Create a temporary policy directory */
53 static const char *gp_tmpdir(TALLOC_CTX
*mem_ctx
)
55 char *gp_dir
= talloc_asprintf(mem_ctx
, "%s/policy", tmpdir());
59 if (gp_dir
== NULL
) return NULL
;
61 if (stat(gp_dir
, &st
) != 0) {
62 rv
= mkdir(gp_dir
, 0755);
64 DEBUG(0, ("Failed to create directory %s: %s\n",
65 gp_dir
, strerror(errno
)));
74 /* This function is called by the smbcli_list function */
75 static void gp_list_helper (struct clilist_file_info
*info
, const char *mask
,
78 struct gp_list_state
*state
= list_state_ptr
;
81 /* Ignore . and .. directory entries */
82 if (strcmp(info
->name
, ".") == 0 || strcmp(info
->name
, "..") == 0) {
86 /* Safety check against ../.. in filenames which may occur on non-POSIX
88 if (strstr(info
->name
, "../")) {
92 rel_path
= talloc_asprintf(state
, "%s\\%s", state
->cur_rel_path
, info
->name
);
93 if (rel_path
== NULL
) return;
95 /* Append entry to file list */
96 state
->list
.files
= talloc_realloc(state
, state
->list
.files
,
98 state
->list
.num_files
+ 1);
99 if (state
->list
.files
== NULL
) return;
101 state
->list
.files
[state
->list
.num_files
].rel_path
= rel_path
;
104 if (info
->attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
105 state
->list
.files
[state
->list
.num_files
].is_directory
= true;
106 state
->list
.num_files
++;
108 /* Recurse into this directory if the depth is below the maximum */
109 if (state
->depth
< GP_MAX_DEPTH
) {
110 gp_do_list(rel_path
, state
);
116 state
->list
.files
[state
->list
.num_files
].is_directory
= false;
117 state
->list
.num_files
++;
122 static NTSTATUS
gp_do_list (const char *rel_path
, struct gp_list_state
*state
)
127 const char *old_rel_path
;
129 attributes
= FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
|
130 FILE_ATTRIBUTE_DIRECTORY
;
132 /* Update the relative paths, while buffering the parent */
133 old_rel_path
= state
->cur_rel_path
;
134 state
->cur_rel_path
= rel_path
;
137 /* Get the current mask */
138 mask
= talloc_asprintf(state
, "%s%s\\*", state
->share_path
, rel_path
);
139 NT_STATUS_HAVE_NO_MEMORY(mask
);
140 rv
= smbcli_list(state
->tree
, mask
, attributes
, gp_list_helper
, state
);
143 /* Go back to the state of the parent */
144 state
->cur_rel_path
= old_rel_path
;
148 return NT_STATUS_UNSUCCESSFUL
;
153 static NTSTATUS
gp_cli_connect(struct gp_context
*gp_ctx
)
155 struct smbcli_options options
;
156 struct smbcli_session_options session_options
;
158 if (gp_ctx
->cli
!= NULL
)
161 gp_ctx
->cli
= smbcli_state_init(gp_ctx
);
163 lpcfg_smbcli_options(gp_ctx
->lp_ctx
, &options
);
164 lpcfg_smbcli_session_options(gp_ctx
->lp_ctx
, &session_options
);
166 return smbcli_full_connection(gp_ctx
,
168 gp_ctx
->active_dc
->name
,
169 lpcfg_smb_ports(gp_ctx
->lp_ctx
),
172 lpcfg_socket_options(gp_ctx
->lp_ctx
),
174 lpcfg_resolve_context(gp_ctx
->lp_ctx
),
178 lpcfg_gensec_settings(gp_ctx
, gp_ctx
->lp_ctx
));
181 static char * gp_get_share_path(TALLOC_CTX
*mem_ctx
, const char *file_sys_path
)
183 unsigned int i
, bkslash_cnt
;
185 /* Get the path from the share down (\\..\..\(this\stuff) */
186 for (i
= 0, bkslash_cnt
= 0; file_sys_path
[i
] != '\0'; i
++) {
187 if (file_sys_path
[i
] == '\\')
190 if (bkslash_cnt
== 4) {
191 return talloc_strdup(mem_ctx
, &file_sys_path
[i
]);
198 static NTSTATUS
gp_get_file (struct smbcli_tree
*tree
, const char *remote_src
,
199 const char *local_dst
)
201 int fh_remote
, fh_local
;
204 size_t buf_size
= 1024;
208 /* Open the remote file */
209 fh_remote
= smbcli_open(tree
, remote_src
, O_RDONLY
, DENY_NONE
);
210 if (fh_remote
== -1) {
211 DEBUG(0, ("Failed to open remote file: %s\n", remote_src
));
212 return NT_STATUS_UNSUCCESSFUL
;
215 /* Open the local file */
216 fh_local
= open(local_dst
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
217 if (fh_local
== -1) {
218 DEBUG(0, ("Failed to open local file: %s\n", local_dst
));
219 smbcli_close(tree
, fh_remote
);
220 return NT_STATUS_UNSUCCESSFUL
;
223 /* Get the remote file size for error checking */
224 if (NT_STATUS_IS_ERR(smbcli_qfileinfo(tree
, fh_remote
,
225 &attr
, &file_size
, NULL
, NULL
, NULL
, NULL
, NULL
)) &&
226 NT_STATUS_IS_ERR(smbcli_getattrE(tree
, fh_remote
,
227 &attr
, &file_size
, NULL
, NULL
, NULL
))) {
228 DEBUG(0, ("Failed to get remote file size: %s\n", smbcli_errstr(tree
)));
229 smbcli_close(tree
, fh_remote
);
231 return NT_STATUS_UNSUCCESSFUL
;
234 buf
= talloc_zero_array(tree
, uint8_t, buf_size
);
236 smbcli_close(tree
, fh_remote
);
238 return NT_STATUS_NO_MEMORY
;
241 /* Copy the contents of the file */
243 int n
= smbcli_read(tree
, fh_remote
, buf
, nread
, buf_size
);
249 if (write(fh_local
, buf
, n
) != n
) {
250 DEBUG(0, ("Short write while copying file.\n"));
251 smbcli_close(tree
, fh_remote
);
254 return NT_STATUS_UNSUCCESSFUL
;
259 /* Close the files */
260 smbcli_close(tree
, fh_remote
);
265 /* Bytes read should match the file size, or the copy was incomplete */
266 if (nread
!= file_size
) {
267 DEBUG(0, ("Remote/local file size mismatch after copying file: "
268 "%s (remote %zu, local %zu).\n",
269 remote_src
, file_size
, nread
));
270 return NT_STATUS_UNSUCCESSFUL
;
276 static NTSTATUS
gp_get_files(struct smbcli_tree
*tree
, const char *share_path
,
277 const char *local_path
, struct gp_file_list
*list
)
281 char *local_rel_path
, *full_local_path
, *full_remote_path
;
285 mem_ctx
= talloc_new(tree
);
286 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
288 for (i
= 0; i
< list
->num_files
; i
++) {
290 /* Get local path by replacing backslashes with slashes */
291 local_rel_path
= talloc_strdup(mem_ctx
, list
->files
[i
].rel_path
);
292 if (local_rel_path
== NULL
) {
293 TALLOC_FREE(mem_ctx
);
294 return NT_STATUS_NO_MEMORY
;
296 string_replace(local_rel_path
, '\\', '/');
298 full_local_path
= talloc_asprintf(mem_ctx
, "%s%s", local_path
,
300 if (full_local_path
== NULL
) {
301 TALLOC_FREE(mem_ctx
);
302 return NT_STATUS_NO_MEMORY
;
305 /* If the entry is a directory, create it. */
306 if (list
->files
[i
].is_directory
== true) {
307 rv
= mkdir(full_local_path
, 0755);
309 DEBUG(0, ("Failed to create directory %s: %s\n",
310 full_local_path
, strerror(errno
)));
311 talloc_free(mem_ctx
);
312 return NT_STATUS_UNSUCCESSFUL
;
317 full_remote_path
= talloc_asprintf(mem_ctx
, "%s%s", share_path
,
318 list
->files
[i
].rel_path
);
319 if (full_remote_path
== NULL
) {
320 TALLOC_FREE(mem_ctx
);
321 return NT_STATUS_NO_MEMORY
;
325 status
= gp_get_file(tree
, full_remote_path
, full_local_path
);
326 if (!NT_STATUS_IS_OK(status
)) {
327 DEBUG(0, ("Error getting file.\n"));
328 talloc_free(mem_ctx
);
336 NTSTATUS
gp_fetch_gpt (struct gp_context
*gp_ctx
, struct gp_object
*gpo
,
337 const char **ret_local_path
)
340 struct gp_list_state
*state
;
344 const char *local_path
, *share_path
;
346 /* Create a forked memory context, as a base for everything here */
347 mem_ctx
= talloc_new(gp_ctx
);
348 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
350 if (gp_ctx
->cli
== NULL
) {
351 status
= gp_cli_connect(gp_ctx
);
352 if (!NT_STATUS_IS_OK(status
)) {
353 DEBUG(0, ("Failed to create cli connection to DC\n"));
354 talloc_free(mem_ctx
);
359 /* Get the remote path to copy from */
360 share_path
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
361 if (share_path
== NULL
) {
362 TALLOC_FREE(mem_ctx
);
363 return NT_STATUS_NO_MEMORY
;
366 /* Get the local path to copy to */
367 local_path
= talloc_asprintf(gp_ctx
, "%s/%s", gp_tmpdir(mem_ctx
), gpo
->name
);
368 if (local_path
== NULL
) {
369 TALLOC_FREE(mem_ctx
);
370 return NT_STATUS_NO_MEMORY
;
373 /* Prepare the state structure */
374 state
= talloc_zero(mem_ctx
, struct gp_list_state
);
376 TALLOC_FREE(mem_ctx
);
377 return NT_STATUS_NO_MEMORY
;
380 state
->tree
= gp_ctx
->cli
->tree
;
381 state
->share_path
= share_path
;
383 /* Create the GPO dir if it does not exist */
384 if (stat(local_path
, &st
) != 0) {
385 rv
= mkdir(local_path
, 0755);
387 DEBUG(0, ("Could not create local path\n"));
388 talloc_free(mem_ctx
);
389 return NT_STATUS_UNSUCCESSFUL
;
393 /* Get the file list */
394 status
= gp_do_list("", state
);
395 if (!NT_STATUS_IS_OK(status
)) {
396 DEBUG(0, ("Could not list GPO files on remote server\n"));
397 talloc_free(mem_ctx
);
401 /* If the list has no entries there is a problem. */
402 if (state
->list
.num_files
== 0) {
403 DEBUG(0, ("File list is has no entries. Is the GPT directory empty?\n"));
404 talloc_free(mem_ctx
);
405 return NT_STATUS_UNSUCCESSFUL
;
408 /* Fetch the files */
409 status
= gp_get_files(gp_ctx
->cli
->tree
, share_path
, local_path
, &state
->list
);
411 /* Return the local path to the gpo */
412 *ret_local_path
= local_path
;
414 talloc_free(mem_ctx
);
418 static NTSTATUS
push_recursive (struct gp_context
*gp_ctx
, const char *local_path
,
419 const char *remote_path
, int depth
)
422 struct dirent
*dirent
;
423 char *entry_local_path
= NULL
;
424 char *entry_remote_path
= NULL
;
425 int local_fd
= -1, remote_fd
= -1;
427 ssize_t nread
, total_read
;
428 ssize_t nwrite
, total_write
;
432 dir
= opendir(local_path
);
434 DEBUG(0, ("Failed to open directory: %s\n", local_path
));
435 return NT_STATUS_UNSUCCESSFUL
;
438 while ((dirent
= readdir(dir
)) != NULL
) {
439 if (ISDOT(dirent
->d_name
) || ISDOTDOT(dirent
->d_name
)) {
443 entry_local_path
= talloc_asprintf(gp_ctx
, "%s/%s", local_path
,
445 if (entry_local_path
== NULL
) {
446 status
= NT_STATUS_NO_MEMORY
;
450 entry_remote_path
= talloc_asprintf(gp_ctx
, "%s\\%s",
451 remote_path
, dirent
->d_name
);
452 if (entry_remote_path
== NULL
) {
453 status
= NT_STATUS_NO_MEMORY
;
457 if (stat(entry_local_path
, &s
) != 0) {
458 status
= NT_STATUS_UNSUCCESSFUL
;
461 if (s
.st_mode
& S_IFDIR
) {
462 DEBUG(6, ("Pushing directory %s to %s on sysvol\n",
463 entry_local_path
, entry_remote_path
));
464 status
= smbcli_mkdir(gp_ctx
->cli
->tree
,
466 if (!NT_STATUS_IS_OK(status
)) {
469 if (depth
< GP_MAX_DEPTH
) {
470 status
= push_recursive(gp_ctx
,
474 if (!NT_STATUS_IS_OK(status
)) {
479 DEBUG(6, ("Pushing file %s to %s on sysvol\n",
480 entry_local_path
, entry_remote_path
));
481 remote_fd
= smbcli_open(gp_ctx
->cli
->tree
,
486 DEBUG(0, ("Failed to create remote file: %s\n",
488 status
= NT_STATUS_UNSUCCESSFUL
;
491 local_fd
= open(entry_local_path
, O_RDONLY
);
493 DEBUG(0, ("Failed to open local file: %s\n",
495 status
= NT_STATUS_UNSUCCESSFUL
;
500 while ((nread
= read(local_fd
, buf
, sizeof(buf
)))) {
502 DBG_ERR("read failed with errno %s\n",
504 status
= NT_STATUS_UNSUCCESSFUL
;
507 nwrite
= smbcli_write(gp_ctx
->cli
->tree
,
511 status
= NT_STATUS_UNSUCCESSFUL
;
515 total_write
+= nwrite
;
517 if (total_read
!= total_write
) {
518 /* Weird and should not happen */
519 status
= NT_STATUS_UNEXPECTED_IO_ERROR
;
525 smbcli_close(gp_ctx
->cli
->tree
, remote_fd
);
528 TALLOC_FREE(entry_local_path
);
529 TALLOC_FREE(entry_remote_path
);
532 status
= NT_STATUS_OK
;
534 if (local_fd
!= -1) {
537 if (remote_fd
!= -1) {
538 smbcli_close(gp_ctx
->cli
->tree
, remote_fd
);
540 talloc_free(entry_local_path
);
541 talloc_free(entry_remote_path
);
550 NTSTATUS
gp_push_gpt(struct gp_context
*gp_ctx
, const char *local_path
,
551 const char *file_sys_path
)
556 if (gp_ctx
->cli
== NULL
) {
557 status
= gp_cli_connect(gp_ctx
);
558 if (!NT_STATUS_IS_OK(status
)) {
559 DEBUG(0, ("Failed to create cli connection to DC\n"));
563 share_path
= gp_get_share_path(gp_ctx
, file_sys_path
);
565 DEBUG(5, ("Copying %s to %s on sysvol\n", local_path
, share_path
));
567 smbcli_mkdir(gp_ctx
->cli
->tree
, share_path
);
569 status
= push_recursive(gp_ctx
, local_path
, share_path
, 0);
571 talloc_free(share_path
);
575 NTSTATUS
gp_create_gpt(struct gp_context
*gp_ctx
, const char *name
,
576 const char *file_sys_path
)
579 const char *tmp_dir
, *policy_dir
, *tmp_str
;
583 const char *file_content
= "[General]\r\nVersion=0\r\n";
585 /* Create a forked memory context, as a base for everything here */
586 mem_ctx
= talloc_new(gp_ctx
);
587 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
589 tmp_dir
= gp_tmpdir(mem_ctx
);
590 NT_STATUS_HAVE_NO_MEMORY(tmp_dir
);
591 policy_dir
= talloc_asprintf(mem_ctx
, "%s/%s", tmp_dir
, name
);
592 NT_STATUS_HAVE_NO_MEMORY(policy_dir
);
594 /* Create the directories */
596 rv
= mkdir(policy_dir
, 0755);
598 DEBUG(0, ("Could not create the policy dir: %s\n", policy_dir
));
599 talloc_free(mem_ctx
);
600 return NT_STATUS_UNSUCCESSFUL
;
603 tmp_str
= talloc_asprintf(mem_ctx
, "%s/User", policy_dir
);
604 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
605 rv
= mkdir(tmp_str
, 0755);
607 DEBUG(0, ("Could not create the User dir: %s\n", tmp_str
));
608 talloc_free(mem_ctx
);
609 return NT_STATUS_UNSUCCESSFUL
;
612 tmp_str
= talloc_asprintf(mem_ctx
, "%s/Machine", policy_dir
);
613 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
614 rv
= mkdir(tmp_str
, 0755);
616 DEBUG(0, ("Could not create the Machine dir: %s\n", tmp_str
));
617 talloc_free(mem_ctx
);
618 return NT_STATUS_UNSUCCESSFUL
;
621 /* Create a GPT.INI with version 0 */
623 tmp_str
= talloc_asprintf(mem_ctx
, "%s/GPT.INI", policy_dir
);
624 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
625 fd
= open(tmp_str
, O_CREAT
| O_WRONLY
, 0644);
627 DEBUG(0, ("Could not create the GPT.INI: %s\n", tmp_str
));
628 talloc_free(mem_ctx
);
629 return NT_STATUS_UNSUCCESSFUL
;
632 rv
= write(fd
, file_content
, strlen(file_content
));
634 if (rv
!= strlen(file_content
)) {
635 DEBUG(0, ("Short write in GPT.INI\n"));
636 talloc_free(mem_ctx
);
637 return NT_STATUS_UNSUCCESSFUL
;
640 /* Upload the GPT to the sysvol share on a DC */
641 status
= gp_push_gpt(gp_ctx
, policy_dir
, file_sys_path
);
642 if (!NT_STATUS_IS_OK(status
)) {
643 talloc_free(mem_ctx
);
647 talloc_free(mem_ctx
);
651 NTSTATUS
gp_set_gpt_security_descriptor(struct gp_context
*gp_ctx
,
652 struct gp_object
*gpo
,
653 struct security_descriptor
*sd
)
657 union smb_setfileinfo fileinfo
;
659 union smb_close io_close
;
661 /* Create a connection to sysvol if it is not already there */
662 if (gp_ctx
->cli
== NULL
) {
663 status
= gp_cli_connect(gp_ctx
);
664 if (!NT_STATUS_IS_OK(status
)) {
665 DEBUG(0, ("Failed to create cli connection to DC\n"));
670 /* Create a forked memory context which can be freed easily */
671 mem_ctx
= talloc_new(gp_ctx
);
672 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
674 /* Open the directory with NTCreate AndX call */
675 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
676 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
677 io
.ntcreatex
.in
.flags
= 0;
678 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
679 io
.ntcreatex
.in
.create_options
= 0;
680 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
681 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
682 NTCREATEX_SHARE_ACCESS_WRITE
;
683 io
.ntcreatex
.in
.alloc_size
= 0;
684 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
685 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
686 io
.ntcreatex
.in
.security_flags
= 0;
687 io
.ntcreatex
.in
.fname
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
688 status
= smb_raw_open(gp_ctx
->cli
->tree
, mem_ctx
, &io
);
689 if (!NT_STATUS_IS_OK(status
)) {
690 DEBUG(0, ("Can't open GPT directory\n"));
691 talloc_free(mem_ctx
);
695 /* Set the security descriptor on the directory */
696 fileinfo
.generic
.level
= RAW_SFILEINFO_SEC_DESC
;
697 fileinfo
.set_secdesc
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
698 fileinfo
.set_secdesc
.in
.secinfo_flags
= SECINFO_PROTECTED_DACL
|
702 fileinfo
.set_secdesc
.in
.sd
= sd
;
703 status
= smb_raw_setfileinfo(gp_ctx
->cli
->tree
, &fileinfo
);
704 if (!NT_STATUS_IS_OK(status
)) {
705 DEBUG(0, ("Failed to set security descriptor on the GPT\n"));
706 talloc_free(mem_ctx
);
710 /* Close the directory */
711 io_close
.close
.level
= RAW_CLOSE_CLOSE
;
712 io_close
.close
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
713 io_close
.close
.in
.write_time
= 0;
714 status
= smb_raw_close(gp_ctx
->cli
->tree
, &io_close
);
715 if (!NT_STATUS_IS_OK(status
)) {
716 DEBUG(0, ("Failed to close directory\n"));
717 talloc_free(mem_ctx
);
721 talloc_free(mem_ctx
);