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/filesys.h"
21 #include "lib/policy/policy.h"
22 #include "libcli/raw/smb.h"
23 #include "libcli/libcli.h"
24 #include "param/param.h"
25 #include "libcli/resolve/resolve.h"
26 #include "libcli/raw/libcliraw.h"
30 #define GP_MAX_DEPTH 25
32 struct gp_file_entry
{
38 struct gp_file_entry
*files
;
40 struct gp_list_state
{
41 struct smbcli_tree
*tree
;
43 const char *cur_rel_path
;
44 const char *share_path
;
46 struct gp_file_list list
;
49 static NTSTATUS
gp_do_list(const char *, struct gp_list_state
*);
51 /* Create a temporary policy directory */
52 static const char *gp_tmpdir(TALLOC_CTX
*mem_ctx
)
54 char *gp_dir
= talloc_asprintf(mem_ctx
, "%s/policy", tmpdir());
58 if (gp_dir
== NULL
) return NULL
;
60 if (stat(gp_dir
, &st
) != 0) {
61 rv
= mkdir(gp_dir
, 0755);
63 DEBUG(0, ("Failed to create directory %s: %s\n",
64 gp_dir
, strerror(errno
)));
73 /* This function is called by the smbcli_list function */
74 static void gp_list_helper (struct clilist_file_info
*info
, const char *mask
,
77 struct gp_list_state
*state
= list_state_ptr
;
80 /* Ignore . and .. directory entries */
81 if (strcmp(info
->name
, ".") == 0 || strcmp(info
->name
, "..") == 0) {
85 /* Safety check against ../.. in filenames which may occur on non-POSIX
87 if (strstr(info
->name
, "../")) {
91 rel_path
= talloc_asprintf(state
, "%s\\%s", state
->cur_rel_path
, info
->name
);
92 if (rel_path
== NULL
) return;
94 /* Append entry to file list */
95 state
->list
.files
= talloc_realloc(state
, state
->list
.files
,
97 state
->list
.num_files
+ 1);
98 if (state
->list
.files
== NULL
) return;
100 state
->list
.files
[state
->list
.num_files
].rel_path
= rel_path
;
103 if (info
->attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
104 state
->list
.files
[state
->list
.num_files
].is_directory
= true;
105 state
->list
.num_files
++;
107 /* Recurse into this directory if the depth is below the maximum */
108 if (state
->depth
< GP_MAX_DEPTH
) {
109 gp_do_list(rel_path
, state
);
115 state
->list
.files
[state
->list
.num_files
].is_directory
= false;
116 state
->list
.num_files
++;
121 static NTSTATUS
gp_do_list (const char *rel_path
, struct gp_list_state
*state
)
126 const char *old_rel_path
;
128 attributes
= FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
|
129 FILE_ATTRIBUTE_DIRECTORY
;
131 /* Update the relative paths, while buffering the parent */
132 old_rel_path
= state
->cur_rel_path
;
133 state
->cur_rel_path
= rel_path
;
136 /* Get the current mask */
137 mask
= talloc_asprintf(state
, "%s%s\\*", state
->share_path
, rel_path
);
138 NT_STATUS_HAVE_NO_MEMORY(mask
);
139 rv
= smbcli_list(state
->tree
, mask
, attributes
, gp_list_helper
, state
);
142 /* Go back to the state of the parent */
143 state
->cur_rel_path
= old_rel_path
;
147 return NT_STATUS_UNSUCCESSFUL
;
152 static NTSTATUS
gp_cli_connect(struct gp_context
*gp_ctx
)
154 struct smbcli_options options
;
155 struct smbcli_session_options session_options
;
157 if (gp_ctx
->cli
!= NULL
)
160 gp_ctx
->cli
= smbcli_state_init(gp_ctx
);
162 lpcfg_smbcli_options(gp_ctx
->lp_ctx
, &options
);
163 lpcfg_smbcli_session_options(gp_ctx
->lp_ctx
, &session_options
);
165 return smbcli_full_connection(gp_ctx
,
167 gp_ctx
->active_dc
->name
,
168 lpcfg_smb_ports(gp_ctx
->lp_ctx
),
171 lpcfg_socket_options(gp_ctx
->lp_ctx
),
173 lpcfg_resolve_context(gp_ctx
->lp_ctx
),
177 lpcfg_gensec_settings(gp_ctx
, gp_ctx
->lp_ctx
));
180 static char * gp_get_share_path(TALLOC_CTX
*mem_ctx
, const char *file_sys_path
)
182 unsigned int i
, bkslash_cnt
;
184 /* Get the path from the share down (\\..\..\(this\stuff) */
185 for (i
= 0, bkslash_cnt
= 0; file_sys_path
[i
] != '\0'; i
++) {
186 if (file_sys_path
[i
] == '\\')
189 if (bkslash_cnt
== 4) {
190 return talloc_strdup(mem_ctx
, &file_sys_path
[i
]);
197 static NTSTATUS
gp_get_file (struct smbcli_tree
*tree
, const char *remote_src
,
198 const char *local_dst
)
200 int fh_remote
, fh_local
;
203 size_t buf_size
= 1024;
207 /* Open the remote file */
208 fh_remote
= smbcli_open(tree
, remote_src
, O_RDONLY
, DENY_NONE
);
209 if (fh_remote
== -1) {
210 DEBUG(0, ("Failed to open remote file: %s\n", remote_src
));
211 return NT_STATUS_UNSUCCESSFUL
;
214 /* Open the local file */
215 fh_local
= open(local_dst
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
216 if (fh_local
== -1) {
217 DEBUG(0, ("Failed to open local file: %s\n", local_dst
));
218 return NT_STATUS_UNSUCCESSFUL
;
221 /* Get the remote file size for error checking */
222 if (NT_STATUS_IS_ERR(smbcli_qfileinfo(tree
, fh_remote
,
223 &attr
, &file_size
, NULL
, NULL
, NULL
, NULL
, NULL
)) &&
224 NT_STATUS_IS_ERR(smbcli_getattrE(tree
, fh_remote
,
225 &attr
, &file_size
, NULL
, NULL
, NULL
))) {
226 DEBUG(0, ("Failed to get remote file size: %s\n", smbcli_errstr(tree
)));
227 return NT_STATUS_UNSUCCESSFUL
;
230 buf
= talloc_zero_array(tree
, uint8_t, buf_size
);
231 NT_STATUS_HAVE_NO_MEMORY(buf
);
233 /* Copy the contents of the file */
235 int n
= smbcli_read(tree
, fh_remote
, buf
, nread
, buf_size
);
241 if (write(fh_local
, buf
, n
) != n
) {
242 DEBUG(0, ("Short write while copying file.\n"));
244 return NT_STATUS_UNSUCCESSFUL
;
249 /* Bytes read should match the file size, or the copy was incomplete */
250 if (nread
!= file_size
) {
251 DEBUG(0, ("Remote/local file size mismatch after copying file: "
252 "%s (remote %zu, local %zu).\n",
253 remote_src
, file_size
, nread
));
256 return NT_STATUS_UNSUCCESSFUL
;
259 /* Close the files */
260 smbcli_close(tree
, fh_remote
);
267 static NTSTATUS
gp_get_files(struct smbcli_tree
*tree
, const char *share_path
,
268 const char *local_path
, struct gp_file_list
*list
)
272 char *local_rel_path
, *full_local_path
, *full_remote_path
;
276 mem_ctx
= talloc_new(tree
);
277 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
279 for (i
= 0; i
< list
->num_files
; i
++) {
281 /* Get local path by replacing backslashes with slashes */
282 local_rel_path
= talloc_strdup(mem_ctx
, list
->files
[i
].rel_path
);
283 if (local_rel_path
== NULL
) {
284 TALLOC_FREE(mem_ctx
);
285 return NT_STATUS_NO_MEMORY
;
287 string_replace(local_rel_path
, '\\', '/');
289 full_local_path
= talloc_asprintf(mem_ctx
, "%s%s", local_path
,
291 if (full_local_path
== NULL
) {
292 TALLOC_FREE(mem_ctx
);
293 return NT_STATUS_NO_MEMORY
;
296 /* If the entry is a directory, create it. */
297 if (list
->files
[i
].is_directory
== true) {
298 rv
= mkdir(full_local_path
, 0755);
300 DEBUG(0, ("Failed to create directory %s: %s\n",
301 full_local_path
, strerror(errno
)));
302 talloc_free(mem_ctx
);
303 return NT_STATUS_UNSUCCESSFUL
;
308 full_remote_path
= talloc_asprintf(mem_ctx
, "%s%s", share_path
,
309 list
->files
[i
].rel_path
);
310 if (full_remote_path
== NULL
) {
311 TALLOC_FREE(mem_ctx
);
312 return NT_STATUS_NO_MEMORY
;
316 status
= gp_get_file(tree
, full_remote_path
, full_local_path
);
317 if (!NT_STATUS_IS_OK(status
)) {
318 DEBUG(0, ("Error getting file.\n"));
319 talloc_free(mem_ctx
);
327 NTSTATUS
gp_fetch_gpt (struct gp_context
*gp_ctx
, struct gp_object
*gpo
,
328 const char **ret_local_path
)
331 struct gp_list_state
*state
;
335 const char *local_path
, *share_path
;
337 /* Create a forked memory context, as a base for everything here */
338 mem_ctx
= talloc_new(gp_ctx
);
339 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
341 if (gp_ctx
->cli
== NULL
) {
342 status
= gp_cli_connect(gp_ctx
);
343 if (!NT_STATUS_IS_OK(status
)) {
344 DEBUG(0, ("Failed to create cli connection to DC\n"));
345 talloc_free(mem_ctx
);
350 /* Get the remote path to copy from */
351 share_path
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
352 if (share_path
== NULL
) {
353 TALLOC_FREE(mem_ctx
);
354 return NT_STATUS_NO_MEMORY
;
357 /* Get the local path to copy to */
358 local_path
= talloc_asprintf(gp_ctx
, "%s/%s", gp_tmpdir(mem_ctx
), gpo
->name
);
359 if (local_path
== NULL
) {
360 TALLOC_FREE(mem_ctx
);
361 return NT_STATUS_NO_MEMORY
;
364 /* Prepare the state structure */
365 state
= talloc_zero(mem_ctx
, struct gp_list_state
);
367 TALLOC_FREE(mem_ctx
);
368 return NT_STATUS_NO_MEMORY
;
371 state
->tree
= gp_ctx
->cli
->tree
;
372 state
->share_path
= share_path
;
374 /* Create the GPO dir if it does not exist */
375 if (stat(local_path
, &st
) != 0) {
376 rv
= mkdir(local_path
, 0755);
378 DEBUG(0, ("Could not create local path\n"));
379 talloc_free(mem_ctx
);
380 return NT_STATUS_UNSUCCESSFUL
;
384 /* Get the file list */
385 status
= gp_do_list("", state
);
386 if (!NT_STATUS_IS_OK(status
)) {
387 DEBUG(0, ("Could not list GPO files on remote server\n"));
388 talloc_free(mem_ctx
);
392 /* If the list has no entries there is a problem. */
393 if (state
->list
.num_files
== 0) {
394 DEBUG(0, ("File list is has no entries. Is the GPT directory empty?\n"));
395 talloc_free(mem_ctx
);
396 return NT_STATUS_UNSUCCESSFUL
;
399 /* Fetch the files */
400 status
= gp_get_files(gp_ctx
->cli
->tree
, share_path
, local_path
, &state
->list
);
402 /* Return the local path to the gpo */
403 *ret_local_path
= local_path
;
405 talloc_free(mem_ctx
);
409 static NTSTATUS
push_recursive (struct gp_context
*gp_ctx
, const char *local_path
,
410 const char *remote_path
, int depth
)
413 struct dirent
*dirent
;
414 char *entry_local_path
= NULL
;
415 char *entry_remote_path
= NULL
;
416 int local_fd
, remote_fd
;
418 int nread
, total_read
;
422 dir
= opendir(local_path
);
423 while ((dirent
= readdir(dir
)) != NULL
) {
424 if (strcmp(dirent
->d_name
, ".") == 0 ||
425 strcmp(dirent
->d_name
, "..") == 0) {
429 entry_local_path
= talloc_asprintf(gp_ctx
, "%s/%s", local_path
,
431 if (entry_local_path
== NULL
) {
432 status
= NT_STATUS_NO_MEMORY
;
436 entry_remote_path
= talloc_asprintf(gp_ctx
, "%s\\%s",
437 remote_path
, dirent
->d_name
);
438 if (entry_remote_path
== NULL
) {
439 status
= NT_STATUS_NO_MEMORY
;
443 if (stat(entry_local_path
, &s
) != 0) {
444 status
= NT_STATUS_UNSUCCESSFUL
;
447 if (s
.st_mode
& S_IFDIR
) {
448 DEBUG(6, ("Pushing directory %s to %s on sysvol\n",
449 entry_local_path
, entry_remote_path
));
450 smbcli_mkdir(gp_ctx
->cli
->tree
, entry_remote_path
);
451 if (depth
< GP_MAX_DEPTH
) {
452 push_recursive(gp_ctx
, entry_local_path
,
453 entry_remote_path
, depth
+1);
456 DEBUG(6, ("Pushing file %s to %s on sysvol\n",
457 entry_local_path
, entry_remote_path
));
458 remote_fd
= smbcli_open(gp_ctx
->cli
->tree
,
463 DEBUG(0, ("Failed to create remote file: %s\n",
465 status
= NT_STATUS_UNSUCCESSFUL
;
468 local_fd
= open(entry_local_path
, O_RDONLY
);
470 DEBUG(0, ("Failed to open local file: %s\n",
472 status
= NT_STATUS_UNSUCCESSFUL
;
476 while ((nread
= read(local_fd
, &buf
, sizeof(buf
)))) {
477 smbcli_write(gp_ctx
->cli
->tree
, remote_fd
, 0,
478 &buf
, total_read
, nread
);
483 smbcli_close(gp_ctx
->cli
->tree
, remote_fd
);
485 TALLOC_FREE(entry_local_path
);
486 TALLOC_FREE(entry_remote_path
);
489 status
= NT_STATUS_OK
;
491 talloc_free(entry_local_path
);
492 talloc_free(entry_remote_path
);
501 NTSTATUS
gp_push_gpt(struct gp_context
*gp_ctx
, const char *local_path
,
502 const char *file_sys_path
)
507 if (gp_ctx
->cli
== NULL
) {
508 status
= gp_cli_connect(gp_ctx
);
509 if (!NT_STATUS_IS_OK(status
)) {
510 DEBUG(0, ("Failed to create cli connection to DC\n"));
514 share_path
= gp_get_share_path(gp_ctx
, file_sys_path
);
516 DEBUG(5, ("Copying %s to %s on sysvol\n", local_path
, share_path
));
518 smbcli_mkdir(gp_ctx
->cli
->tree
, share_path
);
520 status
= push_recursive(gp_ctx
, local_path
, share_path
, 0);
522 talloc_free(share_path
);
526 NTSTATUS
gp_create_gpt(struct gp_context
*gp_ctx
, const char *name
,
527 const char *file_sys_path
)
530 const char *tmp_dir
, *policy_dir
, *tmp_str
;
534 const char *file_content
= "[General]\r\nVersion=0\r\n";
536 /* Create a forked memory context, as a base for everything here */
537 mem_ctx
= talloc_new(gp_ctx
);
538 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
540 tmp_dir
= gp_tmpdir(mem_ctx
);
541 NT_STATUS_HAVE_NO_MEMORY(tmp_dir
);
542 policy_dir
= talloc_asprintf(mem_ctx
, "%s/%s", tmp_dir
, name
);
543 NT_STATUS_HAVE_NO_MEMORY(policy_dir
);
545 /* Create the directories */
547 rv
= mkdir(policy_dir
, 0755);
549 DEBUG(0, ("Could not create the policy dir: %s\n", policy_dir
));
550 talloc_free(mem_ctx
);
551 return NT_STATUS_UNSUCCESSFUL
;
554 tmp_str
= talloc_asprintf(mem_ctx
, "%s/User", policy_dir
);
555 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
556 rv
= mkdir(tmp_str
, 0755);
558 DEBUG(0, ("Could not create the User dir: %s\n", tmp_str
));
559 talloc_free(mem_ctx
);
560 return NT_STATUS_UNSUCCESSFUL
;
563 tmp_str
= talloc_asprintf(mem_ctx
, "%s/Machine", policy_dir
);
564 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
565 rv
= mkdir(tmp_str
, 0755);
567 DEBUG(0, ("Could not create the Machine dir: %s\n", tmp_str
));
568 talloc_free(mem_ctx
);
569 return NT_STATUS_UNSUCCESSFUL
;
572 /* Create a GPT.INI with version 0 */
574 tmp_str
= talloc_asprintf(mem_ctx
, "%s/GPT.INI", policy_dir
);
575 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
576 fd
= open(tmp_str
, O_CREAT
| O_WRONLY
, 0644);
578 DEBUG(0, ("Could not create the GPT.INI: %s\n", tmp_str
));
579 talloc_free(mem_ctx
);
580 return NT_STATUS_UNSUCCESSFUL
;
583 rv
= write(fd
, file_content
, strlen(file_content
));
585 if (rv
!= strlen(file_content
)) {
586 DEBUG(0, ("Short write in GPT.INI\n"));
587 talloc_free(mem_ctx
);
588 return NT_STATUS_UNSUCCESSFUL
;
591 /* Upload the GPT to the sysvol share on a DC */
592 status
= gp_push_gpt(gp_ctx
, policy_dir
, file_sys_path
);
593 if (!NT_STATUS_IS_OK(status
)) {
594 talloc_free(mem_ctx
);
598 talloc_free(mem_ctx
);
602 NTSTATUS
gp_set_gpt_security_descriptor(struct gp_context
*gp_ctx
,
603 struct gp_object
*gpo
,
604 struct security_descriptor
*sd
)
608 union smb_setfileinfo fileinfo
;
610 union smb_close io_close
;
612 /* Create a connection to sysvol if it is not already there */
613 if (gp_ctx
->cli
== NULL
) {
614 status
= gp_cli_connect(gp_ctx
);
615 if (!NT_STATUS_IS_OK(status
)) {
616 DEBUG(0, ("Failed to create cli connection to DC\n"));
621 /* Create a forked memory context which can be freed easily */
622 mem_ctx
= talloc_new(gp_ctx
);
623 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
625 /* Open the directory with NTCreate AndX call */
626 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
627 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
628 io
.ntcreatex
.in
.flags
= 0;
629 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
630 io
.ntcreatex
.in
.create_options
= 0;
631 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
632 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
633 NTCREATEX_SHARE_ACCESS_WRITE
;
634 io
.ntcreatex
.in
.alloc_size
= 0;
635 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
636 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
637 io
.ntcreatex
.in
.security_flags
= 0;
638 io
.ntcreatex
.in
.fname
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
639 status
= smb_raw_open(gp_ctx
->cli
->tree
, mem_ctx
, &io
);
640 if (!NT_STATUS_IS_OK(status
)) {
641 DEBUG(0, ("Can't open GPT directory\n"));
642 talloc_free(mem_ctx
);
646 /* Set the security descriptor on the directory */
647 fileinfo
.generic
.level
= RAW_FILEINFO_SEC_DESC
;
648 fileinfo
.set_secdesc
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
649 fileinfo
.set_secdesc
.in
.secinfo_flags
= SECINFO_PROTECTED_DACL
|
653 fileinfo
.set_secdesc
.in
.sd
= sd
;
654 status
= smb_raw_setfileinfo(gp_ctx
->cli
->tree
, &fileinfo
);
655 if (!NT_STATUS_IS_OK(status
)) {
656 DEBUG(0, ("Failed to set security descriptor on the GPT\n"));
657 talloc_free(mem_ctx
);
661 /* Close the directory */
662 io_close
.close
.level
= RAW_CLOSE_CLOSE
;
663 io_close
.close
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
664 io_close
.close
.in
.write_time
= 0;
665 status
= smb_raw_close(gp_ctx
->cli
->tree
, &io_close
);
666 if (!NT_STATUS_IS_OK(status
)) {
667 DEBUG(0, ("Failed to close directory\n"));
668 talloc_free(mem_ctx
);
672 talloc_free(mem_ctx
);