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 "lib/policy/policy.h"
21 #include "libcli/raw/smb.h"
22 #include "libcli/libcli.h"
23 #include "param/param.h"
24 #include "libcli/resolve/resolve.h"
25 #include "libcli/raw/libcliraw.h"
32 #define GP_MAX_DEPTH 25
34 struct gp_file_entry
{
40 struct gp_file_entry
*files
;
42 struct gp_list_state
{
43 struct smbcli_tree
*tree
;
45 const char *cur_rel_path
;
46 const char *share_path
;
48 struct gp_file_list list
;
51 static NTSTATUS
gp_do_list(const char *, struct gp_list_state
*);
53 /* Create a temporary policy directory */
54 static const char *gp_tmpdir(TALLOC_CTX
*mem_ctx
)
56 char *gp_dir
= talloc_asprintf(mem_ctx
, "%s/policy", tmpdir());
60 if (gp_dir
== NULL
) return NULL
;
62 if (stat(gp_dir
, &st
) != 0) {
63 rv
= mkdir(gp_dir
, 0755);
65 DEBUG(0, ("Failed to create directory %s: %s\n",
66 gp_dir
, strerror(errno
)));
75 /* This function is called by the smbcli_list function */
76 static void gp_list_helper (struct clilist_file_info
*info
, const char *mask
,
79 struct gp_list_state
*state
= list_state_ptr
;
82 /* Ignore . and .. directory entries */
83 if (strcmp(info
->name
, ".") == 0 || strcmp(info
->name
, "..") == 0) {
87 /* Safety check against ../.. in filenames which may occur on non-POSIX
89 if (strstr(info
->name
, "../")) {
93 rel_path
= talloc_asprintf(state
, "%s\\%s", state
->cur_rel_path
, info
->name
);
94 if (rel_path
== NULL
) return;
96 /* Append entry to file list */
97 state
->list
.files
= talloc_realloc(state
, state
->list
.files
,
99 state
->list
.num_files
+ 1);
100 if (state
->list
.files
== NULL
) return;
102 state
->list
.files
[state
->list
.num_files
].rel_path
= rel_path
;
105 if (info
->attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
106 state
->list
.files
[state
->list
.num_files
].is_directory
= true;
107 state
->list
.num_files
++;
109 /* Recurse into this directory if the depth is below the maximum */
110 if (state
->depth
< GP_MAX_DEPTH
) {
111 gp_do_list(rel_path
, state
);
117 state
->list
.files
[state
->list
.num_files
].is_directory
= false;
118 state
->list
.num_files
++;
123 static NTSTATUS
gp_do_list (const char *rel_path
, struct gp_list_state
*state
)
128 const char *old_rel_path
;
130 attributes
= FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
|
131 FILE_ATTRIBUTE_DIRECTORY
;
133 /* Update the relative paths, while buffering the parent */
134 old_rel_path
= state
->cur_rel_path
;
135 state
->cur_rel_path
= rel_path
;
138 /* Get the current mask */
139 mask
= talloc_asprintf(state
, "%s%s\\*", state
->share_path
, rel_path
);
140 NT_STATUS_HAVE_NO_MEMORY(mask
);
141 rv
= smbcli_list(state
->tree
, mask
, attributes
, gp_list_helper
, state
);
144 /* Go back to the state of the parent */
145 state
->cur_rel_path
= old_rel_path
;
149 return NT_STATUS_UNSUCCESSFUL
;
154 static NTSTATUS
gp_cli_connect(struct gp_context
*gp_ctx
)
156 struct smbcli_options options
;
157 struct smbcli_session_options session_options
;
159 if (gp_ctx
->cli
!= NULL
)
162 gp_ctx
->cli
= smbcli_state_init(gp_ctx
);
164 lp_smbcli_options(gp_ctx
->lp_ctx
, &options
);
165 lp_smbcli_session_options(gp_ctx
->lp_ctx
, &session_options
);
168 return smbcli_full_connection(gp_ctx
,
170 gp_ctx
->active_dc
.name
,
171 lp_smb_ports(gp_ctx
->lp_ctx
),
174 lp_socket_options(gp_ctx
->lp_ctx
),
176 lp_resolve_context(gp_ctx
->lp_ctx
),
180 lp_gensec_settings(gp_ctx
, gp_ctx
->lp_ctx
));
185 static char * gp_get_share_path(TALLOC_CTX
*mem_ctx
, const char *file_sys_path
)
187 unsigned int i
, bkslash_cnt
;
189 /* Get the path from the share down (\\..\..\(this\stuff) */
190 for (i
= 0, bkslash_cnt
= 0; file_sys_path
[i
] != '\0'; i
++) {
191 if (file_sys_path
[i
] == '\\')
194 if (bkslash_cnt
== 4) {
195 return talloc_strdup(mem_ctx
, &file_sys_path
[i
]);
202 static NTSTATUS
gp_get_file (struct smbcli_tree
*tree
, const char *remote_src
,
203 const char *local_dst
)
205 int fh_remote
, fh_local
;
208 size_t buf_size
= 1024;
212 /* Open the remote file */
213 fh_remote
= smbcli_open(tree
, remote_src
, O_RDONLY
, DENY_NONE
);
214 if (fh_remote
== -1) {
215 DEBUG(0, ("Failed to open remote file: %s\n", remote_src
));
216 return NT_STATUS_UNSUCCESSFUL
;
219 /* Open the local file */
220 fh_local
= open(local_dst
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
221 if (fh_local
== -1) {
222 DEBUG(0, ("Failed to open local file: %s\n", local_dst
));
223 return NT_STATUS_UNSUCCESSFUL
;
226 /* Get the remote file size for error checking */
227 if (NT_STATUS_IS_ERR(smbcli_qfileinfo(tree
, fh_remote
,
228 &attr
, &file_size
, NULL
, NULL
, NULL
, NULL
, NULL
)) &&
229 NT_STATUS_IS_ERR(smbcli_getattrE(tree
, fh_remote
,
230 &attr
, &file_size
, NULL
, NULL
, NULL
))) {
231 DEBUG(0, ("Failed to get remote file size: %s\n", smbcli_errstr(tree
)));
232 return NT_STATUS_UNSUCCESSFUL
;
235 buf
= talloc_zero_array(tree
, uint8_t, buf_size
);
236 NT_STATUS_HAVE_NO_MEMORY(buf
);
238 /* Copy the contents of the file */
240 int n
= smbcli_read(tree
, fh_remote
, buf
, nread
, buf_size
);
246 if (write(fh_local
, buf
, n
) != n
) {
247 DEBUG(0, ("Short write while copying file.\n"));
249 return NT_STATUS_UNSUCCESSFUL
;
254 /* Bytes read should match the file size, or the copy was incomplete */
255 if (nread
!= file_size
) {
256 DEBUG(0, ("Remote/local file size mismatch after copying file: "
257 "%s (remote %ld, local %ld).\n",
258 remote_src
, file_size
, nread
));
260 return NT_STATUS_UNSUCCESSFUL
;
263 /* Close the files */
264 smbcli_close(tree
, fh_remote
);
271 static NTSTATUS
gp_get_files(struct smbcli_tree
*tree
, const char *share_path
,
272 const char *local_path
, struct gp_file_list
*list
)
276 char *local_rel_path
, *full_local_path
, *full_remote_path
;
280 mem_ctx
= talloc_new(tree
);
281 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
283 for (i
= 0; i
< list
->num_files
; i
++) {
285 /* Get local path by replacing backslashes with slashes */
286 local_rel_path
= talloc_strdup(mem_ctx
, list
->files
[i
].rel_path
);
287 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(local_rel_path
, mem_ctx
);
288 string_replace(local_rel_path
, '\\', '/');
290 full_local_path
= talloc_asprintf(mem_ctx
, "%s%s", local_path
,
292 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(full_local_path
, mem_ctx
);
294 /* If the entry is a directory, create it. */
295 if (list
->files
[i
].is_directory
== true) {
296 rv
= mkdir(full_local_path
, 0755);
298 DEBUG(0, ("Failed to create directory %s: %s\n",
299 full_local_path
, strerror(errno
)));
300 talloc_free(mem_ctx
);
301 return NT_STATUS_UNSUCCESSFUL
;
306 full_remote_path
= talloc_asprintf(mem_ctx
, "%s%s", share_path
,
307 list
->files
[i
].rel_path
);
308 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(full_remote_path
, mem_ctx
);
311 status
= gp_get_file(tree
, full_remote_path
, full_local_path
);
312 if (!NT_STATUS_IS_OK(status
)) {
313 DEBUG(0, ("Error getting file.\n"));
314 talloc_free(mem_ctx
);
322 NTSTATUS
gp_fetch_gpt (struct gp_context
*gp_ctx
, struct gp_object
*gpo
,
323 const char **ret_local_path
)
326 struct gp_list_state
*state
;
330 const char *local_path
, *share_path
;
332 /* Create a forked memory context, as a base for everything here */
333 mem_ctx
= talloc_new(gp_ctx
);
334 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
336 if (gp_ctx
->cli
== NULL
) {
337 status
= gp_cli_connect(gp_ctx
);
338 if (!NT_STATUS_IS_OK(status
)) {
339 DEBUG(0, ("Failed to create cli connection to DC\n"));
340 talloc_free(mem_ctx
);
345 /* Get the remote path to copy from */
346 share_path
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
347 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(share_path
, mem_ctx
);
349 /* Get the local path to copy to */
350 local_path
= talloc_asprintf(gp_ctx
, "%s/%s", gp_tmpdir(mem_ctx
), gpo
->name
);
351 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(local_path
, mem_ctx
);
353 /* Prepare the state structure */
354 state
= talloc_zero(mem_ctx
, struct gp_list_state
);
355 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(state
, mem_ctx
);
357 state
->tree
= gp_ctx
->cli
->tree
;
358 state
->share_path
= share_path
;
360 /* Create the GPO dir if it does not exist */
361 if (stat(local_path
, &st
) != 0) {
362 rv
= mkdir(local_path
, 0755);
364 DEBUG(0, ("Could not create local path\n"));
365 talloc_free(mem_ctx
);
366 return NT_STATUS_UNSUCCESSFUL
;
370 /* Get the file list */
371 status
= gp_do_list("", state
);
372 if (!NT_STATUS_IS_OK(status
)) {
373 DEBUG(0, ("Could not list GPO files on remote server\n"));
374 talloc_free(mem_ctx
);
378 /* If the list has no entries there is a problem. */
379 if (state
->list
.num_files
== 0) {
380 DEBUG(0, ("File list is has no entries. Is the GPT directory empty?\n"));
381 talloc_free(mem_ctx
);
382 return NT_STATUS_UNSUCCESSFUL
;
385 /* Fetch the files */
386 status
= gp_get_files(gp_ctx
->cli
->tree
, share_path
, local_path
, &state
->list
);
388 /* Return the local path to the gpo */
389 *ret_local_path
= local_path
;
391 talloc_free(mem_ctx
);
395 static NTSTATUS
push_recursive (struct gp_context
*gp_ctx
, const char *local_path
,
396 const char *remote_path
, int depth
)
399 struct dirent
*dirent
;
400 char *entry_local_path
;
401 char *entry_remote_path
;
402 int local_fd
, remote_fd
;
404 int nread
, total_read
;
406 dir
= opendir(local_path
);
407 while ((dirent
= readdir(dir
)) != NULL
) {
408 if (strcmp(dirent
->d_name
, ".") == 0 ||
409 strcmp(dirent
->d_name
, "..") == 0) {
413 entry_local_path
= talloc_asprintf(gp_ctx
, "%s/%s", local_path
,
415 NT_STATUS_HAVE_NO_MEMORY(entry_local_path
);
417 entry_remote_path
= talloc_asprintf(gp_ctx
, "%s\\%s",
418 remote_path
, dirent
->d_name
);
419 NT_STATUS_HAVE_NO_MEMORY(entry_remote_path
);
421 if (dirent
->d_type
== DT_DIR
) {
422 DEBUG(6, ("Pushing directory %s to %s on sysvol\n",
423 entry_local_path
, entry_remote_path
));
424 smbcli_mkdir(gp_ctx
->cli
->tree
, entry_remote_path
);
425 if (depth
< GP_MAX_DEPTH
) {
426 push_recursive(gp_ctx
, entry_local_path
,
427 entry_remote_path
, depth
+1);
430 DEBUG(6, ("Pushing file %s to %s on sysvol\n",
431 entry_local_path
, entry_remote_path
));
432 remote_fd
= smbcli_open(gp_ctx
->cli
->tree
,
437 talloc_free(entry_local_path
);
438 talloc_free(entry_remote_path
);
439 DEBUG(0, ("Failed to create remote file: %s\n",
441 return NT_STATUS_UNSUCCESSFUL
;
443 local_fd
= open(entry_local_path
, O_RDONLY
);
445 talloc_free(entry_local_path
);
446 talloc_free(entry_remote_path
);
447 DEBUG(0, ("Failed to open local file: %s\n",
449 return NT_STATUS_UNSUCCESSFUL
;
452 while ((nread
= read(local_fd
, &buf
, sizeof(buf
)))) {
453 smbcli_write(gp_ctx
->cli
->tree
, remote_fd
, 0,
454 &buf
, total_read
, nread
);
459 smbcli_close(gp_ctx
->cli
->tree
, remote_fd
);
461 talloc_free(entry_local_path
);
462 talloc_free(entry_remote_path
);
471 NTSTATUS
gp_push_gpt(struct gp_context
*gp_ctx
, const char *local_path
,
472 const char *file_sys_path
)
477 if (gp_ctx
->cli
== NULL
) {
478 status
= gp_cli_connect(gp_ctx
);
479 if (!NT_STATUS_IS_OK(status
)) {
480 DEBUG(0, ("Failed to create cli connection to DC\n"));
484 share_path
= gp_get_share_path(gp_ctx
, file_sys_path
);
486 DEBUG(5, ("Copying %s to %s on sysvol\n", local_path
, share_path
));
488 smbcli_mkdir(gp_ctx
->cli
->tree
, share_path
);
490 status
= push_recursive(gp_ctx
, local_path
, share_path
, 0);
492 talloc_free(share_path
);
496 NTSTATUS
gp_create_gpt(struct gp_context
*gp_ctx
, const char *name
,
497 const char *file_sys_path
)
500 const char *tmp_dir
, *policy_dir
, *tmp_str
;
504 const char *file_content
= "[General]\r\nVersion=0\r\n";
506 /* Create a forked memory context, as a base for everything here */
507 mem_ctx
= talloc_new(gp_ctx
);
508 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
510 tmp_dir
= gp_tmpdir(mem_ctx
);
511 NT_STATUS_HAVE_NO_MEMORY(tmp_dir
);
512 policy_dir
= talloc_asprintf(mem_ctx
, "%s/%s", tmp_dir
, name
);
513 NT_STATUS_HAVE_NO_MEMORY(policy_dir
);
515 /* Create the directories */
517 rv
= mkdir(policy_dir
, 0755);
519 DEBUG(0, ("Could not create the policy dir: %s\n", policy_dir
));
520 talloc_free(mem_ctx
);
521 return NT_STATUS_UNSUCCESSFUL
;
524 tmp_str
= talloc_asprintf(mem_ctx
, "%s/User", policy_dir
);
525 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
526 rv
= mkdir(tmp_str
, 0755);
528 DEBUG(0, ("Could not create the User dir: %s\n", tmp_str
));
529 talloc_free(mem_ctx
);
530 return NT_STATUS_UNSUCCESSFUL
;
533 tmp_str
= talloc_asprintf(mem_ctx
, "%s/Machine", policy_dir
);
534 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
535 rv
= mkdir(tmp_str
, 0755);
537 DEBUG(0, ("Could not create the Machine dir: %s\n", tmp_str
));
538 talloc_free(mem_ctx
);
539 return NT_STATUS_UNSUCCESSFUL
;
542 /* Create a GPT.INI with version 0 */
544 tmp_str
= talloc_asprintf(mem_ctx
, "%s/GPT.INI", policy_dir
);
545 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
546 fd
= open(tmp_str
, O_CREAT
| O_WRONLY
, 0644);
548 DEBUG(0, ("Could not create the GPT.INI: %s\n", tmp_str
));
549 talloc_free(mem_ctx
);
550 return NT_STATUS_UNSUCCESSFUL
;
553 rv
= write(fd
, file_content
, strlen(file_content
));
554 if (rv
!= strlen(file_content
)) {
555 DEBUG(0, ("Short write in GPT.INI\n"));
556 talloc_free(mem_ctx
);
557 return NT_STATUS_UNSUCCESSFUL
;
562 /* Upload the GPT to the sysvol share on a DC */
563 status
= gp_push_gpt(gp_ctx
, policy_dir
, file_sys_path
);
564 if (!NT_STATUS_IS_OK(status
)) {
565 talloc_free(mem_ctx
);
569 talloc_free(mem_ctx
);
573 NTSTATUS
gp_set_gpt_security_descriptor(struct gp_context
*gp_ctx
,
574 struct gp_object
*gpo
,
575 struct security_descriptor
*sd
)
579 union smb_setfileinfo fileinfo
;
581 union smb_close io_close
;
583 /* Create a connection to sysvol if it is not already there */
584 if (gp_ctx
->cli
== NULL
) {
585 status
= gp_cli_connect(gp_ctx
);
586 if (!NT_STATUS_IS_OK(status
)) {
587 DEBUG(0, ("Failed to create cli connection to DC\n"));
592 /* Create a forked memory context which can be freed easily */
593 mem_ctx
= talloc_new(gp_ctx
);
594 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
596 /* Open the directory with NTCreate AndX call */
597 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
598 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
599 io
.ntcreatex
.in
.flags
= 0;
600 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
601 io
.ntcreatex
.in
.create_options
= 0;
602 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
603 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
604 NTCREATEX_SHARE_ACCESS_WRITE
;
605 io
.ntcreatex
.in
.alloc_size
= 0;
606 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
607 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
608 io
.ntcreatex
.in
.security_flags
= 0;
609 io
.ntcreatex
.in
.fname
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
610 status
= smb_raw_open(gp_ctx
->cli
->tree
, mem_ctx
, &io
);
611 if (!NT_STATUS_IS_OK(status
)) {
612 DEBUG(0, ("Can't open GPT directory\n"));
613 talloc_free(mem_ctx
);
617 /* Set the security descriptor on the directory */
618 fileinfo
.generic
.level
= RAW_FILEINFO_SEC_DESC
;
619 fileinfo
.set_secdesc
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
620 fileinfo
.set_secdesc
.in
.secinfo_flags
= SECINFO_PROTECTED_DACL
|
624 fileinfo
.set_secdesc
.in
.sd
= sd
;
625 status
= smb_raw_setfileinfo(gp_ctx
->cli
->tree
, &fileinfo
);
626 if (!NT_STATUS_IS_OK(status
)) {
627 DEBUG(0, ("Failed to set security descriptor on the GPT\n"));
628 talloc_free(mem_ctx
);
632 /* Close the directory */
633 io_close
.close
.level
= RAW_CLOSE_CLOSE
;
634 io_close
.close
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
635 io_close
.close
.in
.write_time
= 0;
636 status
= smb_raw_close(gp_ctx
->cli
->tree
, &io_close
);
637 if (!NT_STATUS_IS_OK(status
)) {
638 DEBUG(0, ("Failed to close directory\n"));
639 talloc_free(mem_ctx
);
643 talloc_free(mem_ctx
);