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 lpcfg_smbcli_options(gp_ctx
->lp_ctx
, &options
);
165 lpcfg_smbcli_session_options(gp_ctx
->lp_ctx
, &session_options
);
168 return smbcli_full_connection(gp_ctx
,
170 gp_ctx
->active_dc
.name
,
171 lpcfg_smb_ports(gp_ctx
->lp_ctx
),
174 lpcfg_socket_options(gp_ctx
->lp_ctx
),
176 lpcfg_resolve_context(gp_ctx
->lp_ctx
),
180 lpcfg_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 %zu, local %zu).\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
;
407 dir
= opendir(local_path
);
408 while ((dirent
= readdir(dir
)) != NULL
) {
409 if (strcmp(dirent
->d_name
, ".") == 0 ||
410 strcmp(dirent
->d_name
, "..") == 0) {
414 entry_local_path
= talloc_asprintf(gp_ctx
, "%s/%s", local_path
,
416 NT_STATUS_HAVE_NO_MEMORY(entry_local_path
);
418 entry_remote_path
= talloc_asprintf(gp_ctx
, "%s\\%s",
419 remote_path
, dirent
->d_name
);
420 NT_STATUS_HAVE_NO_MEMORY(entry_remote_path
);
422 if (stat(dirent
->d_name
, &s
) != 0) {
423 return NT_STATUS_UNSUCCESSFUL
;
425 if (s
.st_mode
& S_IFDIR
) {
426 DEBUG(6, ("Pushing directory %s to %s on sysvol\n",
427 entry_local_path
, entry_remote_path
));
428 smbcli_mkdir(gp_ctx
->cli
->tree
, entry_remote_path
);
429 if (depth
< GP_MAX_DEPTH
) {
430 push_recursive(gp_ctx
, entry_local_path
,
431 entry_remote_path
, depth
+1);
434 DEBUG(6, ("Pushing file %s to %s on sysvol\n",
435 entry_local_path
, entry_remote_path
));
436 remote_fd
= smbcli_open(gp_ctx
->cli
->tree
,
441 talloc_free(entry_local_path
);
442 talloc_free(entry_remote_path
);
443 DEBUG(0, ("Failed to create remote file: %s\n",
445 return NT_STATUS_UNSUCCESSFUL
;
447 local_fd
= open(entry_local_path
, O_RDONLY
);
449 talloc_free(entry_local_path
);
450 talloc_free(entry_remote_path
);
451 DEBUG(0, ("Failed to open local file: %s\n",
453 return NT_STATUS_UNSUCCESSFUL
;
456 while ((nread
= read(local_fd
, &buf
, sizeof(buf
)))) {
457 smbcli_write(gp_ctx
->cli
->tree
, remote_fd
, 0,
458 &buf
, total_read
, nread
);
463 smbcli_close(gp_ctx
->cli
->tree
, remote_fd
);
465 talloc_free(entry_local_path
);
466 talloc_free(entry_remote_path
);
475 NTSTATUS
gp_push_gpt(struct gp_context
*gp_ctx
, const char *local_path
,
476 const char *file_sys_path
)
481 if (gp_ctx
->cli
== NULL
) {
482 status
= gp_cli_connect(gp_ctx
);
483 if (!NT_STATUS_IS_OK(status
)) {
484 DEBUG(0, ("Failed to create cli connection to DC\n"));
488 share_path
= gp_get_share_path(gp_ctx
, file_sys_path
);
490 DEBUG(5, ("Copying %s to %s on sysvol\n", local_path
, share_path
));
492 smbcli_mkdir(gp_ctx
->cli
->tree
, share_path
);
494 status
= push_recursive(gp_ctx
, local_path
, share_path
, 0);
496 talloc_free(share_path
);
500 NTSTATUS
gp_create_gpt(struct gp_context
*gp_ctx
, const char *name
,
501 const char *file_sys_path
)
504 const char *tmp_dir
, *policy_dir
, *tmp_str
;
508 const char *file_content
= "[General]\r\nVersion=0\r\n";
510 /* Create a forked memory context, as a base for everything here */
511 mem_ctx
= talloc_new(gp_ctx
);
512 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
514 tmp_dir
= gp_tmpdir(mem_ctx
);
515 NT_STATUS_HAVE_NO_MEMORY(tmp_dir
);
516 policy_dir
= talloc_asprintf(mem_ctx
, "%s/%s", tmp_dir
, name
);
517 NT_STATUS_HAVE_NO_MEMORY(policy_dir
);
519 /* Create the directories */
521 rv
= mkdir(policy_dir
, 0755);
523 DEBUG(0, ("Could not create the policy dir: %s\n", policy_dir
));
524 talloc_free(mem_ctx
);
525 return NT_STATUS_UNSUCCESSFUL
;
528 tmp_str
= talloc_asprintf(mem_ctx
, "%s/User", policy_dir
);
529 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
530 rv
= mkdir(tmp_str
, 0755);
532 DEBUG(0, ("Could not create the User dir: %s\n", tmp_str
));
533 talloc_free(mem_ctx
);
534 return NT_STATUS_UNSUCCESSFUL
;
537 tmp_str
= talloc_asprintf(mem_ctx
, "%s/Machine", policy_dir
);
538 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
539 rv
= mkdir(tmp_str
, 0755);
541 DEBUG(0, ("Could not create the Machine dir: %s\n", tmp_str
));
542 talloc_free(mem_ctx
);
543 return NT_STATUS_UNSUCCESSFUL
;
546 /* Create a GPT.INI with version 0 */
548 tmp_str
= talloc_asprintf(mem_ctx
, "%s/GPT.INI", policy_dir
);
549 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
550 fd
= open(tmp_str
, O_CREAT
| O_WRONLY
, 0644);
552 DEBUG(0, ("Could not create the GPT.INI: %s\n", tmp_str
));
553 talloc_free(mem_ctx
);
554 return NT_STATUS_UNSUCCESSFUL
;
557 rv
= write(fd
, file_content
, strlen(file_content
));
558 if (rv
!= strlen(file_content
)) {
559 DEBUG(0, ("Short write in GPT.INI\n"));
560 talloc_free(mem_ctx
);
561 return NT_STATUS_UNSUCCESSFUL
;
566 /* Upload the GPT to the sysvol share on a DC */
567 status
= gp_push_gpt(gp_ctx
, policy_dir
, file_sys_path
);
568 if (!NT_STATUS_IS_OK(status
)) {
569 talloc_free(mem_ctx
);
573 talloc_free(mem_ctx
);
577 NTSTATUS
gp_set_gpt_security_descriptor(struct gp_context
*gp_ctx
,
578 struct gp_object
*gpo
,
579 struct security_descriptor
*sd
)
583 union smb_setfileinfo fileinfo
;
585 union smb_close io_close
;
587 /* Create a connection to sysvol if it is not already there */
588 if (gp_ctx
->cli
== NULL
) {
589 status
= gp_cli_connect(gp_ctx
);
590 if (!NT_STATUS_IS_OK(status
)) {
591 DEBUG(0, ("Failed to create cli connection to DC\n"));
596 /* Create a forked memory context which can be freed easily */
597 mem_ctx
= talloc_new(gp_ctx
);
598 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
600 /* Open the directory with NTCreate AndX call */
601 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
602 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
603 io
.ntcreatex
.in
.flags
= 0;
604 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
605 io
.ntcreatex
.in
.create_options
= 0;
606 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
607 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
608 NTCREATEX_SHARE_ACCESS_WRITE
;
609 io
.ntcreatex
.in
.alloc_size
= 0;
610 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
611 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
612 io
.ntcreatex
.in
.security_flags
= 0;
613 io
.ntcreatex
.in
.fname
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
614 status
= smb_raw_open(gp_ctx
->cli
->tree
, mem_ctx
, &io
);
615 if (!NT_STATUS_IS_OK(status
)) {
616 DEBUG(0, ("Can't open GPT directory\n"));
617 talloc_free(mem_ctx
);
621 /* Set the security descriptor on the directory */
622 fileinfo
.generic
.level
= RAW_FILEINFO_SEC_DESC
;
623 fileinfo
.set_secdesc
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
624 fileinfo
.set_secdesc
.in
.secinfo_flags
= SECINFO_PROTECTED_DACL
|
628 fileinfo
.set_secdesc
.in
.sd
= sd
;
629 status
= smb_raw_setfileinfo(gp_ctx
->cli
->tree
, &fileinfo
);
630 if (!NT_STATUS_IS_OK(status
)) {
631 DEBUG(0, ("Failed to set security descriptor on the GPT\n"));
632 talloc_free(mem_ctx
);
636 /* Close the directory */
637 io_close
.close
.level
= RAW_CLOSE_CLOSE
;
638 io_close
.close
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
639 io_close
.close
.in
.write_time
= 0;
640 status
= smb_raw_close(gp_ctx
->cli
->tree
, &io_close
);
641 if (!NT_STATUS_IS_OK(status
)) {
642 DEBUG(0, ("Failed to close directory\n"));
643 talloc_free(mem_ctx
);
647 talloc_free(mem_ctx
);