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
));
255 return NT_STATUS_UNSUCCESSFUL
;
258 /* Close the files */
259 smbcli_close(tree
, fh_remote
);
266 static NTSTATUS
gp_get_files(struct smbcli_tree
*tree
, const char *share_path
,
267 const char *local_path
, struct gp_file_list
*list
)
271 char *local_rel_path
, *full_local_path
, *full_remote_path
;
275 mem_ctx
= talloc_new(tree
);
276 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
278 for (i
= 0; i
< list
->num_files
; i
++) {
280 /* Get local path by replacing backslashes with slashes */
281 local_rel_path
= talloc_strdup(mem_ctx
, list
->files
[i
].rel_path
);
282 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(local_rel_path
, mem_ctx
);
283 string_replace(local_rel_path
, '\\', '/');
285 full_local_path
= talloc_asprintf(mem_ctx
, "%s%s", local_path
,
287 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(full_local_path
, mem_ctx
);
289 /* If the entry is a directory, create it. */
290 if (list
->files
[i
].is_directory
== true) {
291 rv
= mkdir(full_local_path
, 0755);
293 DEBUG(0, ("Failed to create directory %s: %s\n",
294 full_local_path
, strerror(errno
)));
295 talloc_free(mem_ctx
);
296 return NT_STATUS_UNSUCCESSFUL
;
301 full_remote_path
= talloc_asprintf(mem_ctx
, "%s%s", share_path
,
302 list
->files
[i
].rel_path
);
303 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(full_remote_path
, mem_ctx
);
306 status
= gp_get_file(tree
, full_remote_path
, full_local_path
);
307 if (!NT_STATUS_IS_OK(status
)) {
308 DEBUG(0, ("Error getting file.\n"));
309 talloc_free(mem_ctx
);
317 NTSTATUS
gp_fetch_gpt (struct gp_context
*gp_ctx
, struct gp_object
*gpo
,
318 const char **ret_local_path
)
321 struct gp_list_state
*state
;
325 const char *local_path
, *share_path
;
327 /* Create a forked memory context, as a base for everything here */
328 mem_ctx
= talloc_new(gp_ctx
);
329 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
331 if (gp_ctx
->cli
== NULL
) {
332 status
= gp_cli_connect(gp_ctx
);
333 if (!NT_STATUS_IS_OK(status
)) {
334 DEBUG(0, ("Failed to create cli connection to DC\n"));
335 talloc_free(mem_ctx
);
340 /* Get the remote path to copy from */
341 share_path
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
342 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(share_path
, mem_ctx
);
344 /* Get the local path to copy to */
345 local_path
= talloc_asprintf(gp_ctx
, "%s/%s", gp_tmpdir(mem_ctx
), gpo
->name
);
346 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(local_path
, mem_ctx
);
348 /* Prepare the state structure */
349 state
= talloc_zero(mem_ctx
, struct gp_list_state
);
350 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(state
, mem_ctx
);
352 state
->tree
= gp_ctx
->cli
->tree
;
353 state
->share_path
= share_path
;
355 /* Create the GPO dir if it does not exist */
356 if (stat(local_path
, &st
) != 0) {
357 rv
= mkdir(local_path
, 0755);
359 DEBUG(0, ("Could not create local path\n"));
360 talloc_free(mem_ctx
);
361 return NT_STATUS_UNSUCCESSFUL
;
365 /* Get the file list */
366 status
= gp_do_list("", state
);
367 if (!NT_STATUS_IS_OK(status
)) {
368 DEBUG(0, ("Could not list GPO files on remote server\n"));
369 talloc_free(mem_ctx
);
373 /* If the list has no entries there is a problem. */
374 if (state
->list
.num_files
== 0) {
375 DEBUG(0, ("File list is has no entries. Is the GPT directory empty?\n"));
376 talloc_free(mem_ctx
);
377 return NT_STATUS_UNSUCCESSFUL
;
380 /* Fetch the files */
381 status
= gp_get_files(gp_ctx
->cli
->tree
, share_path
, local_path
, &state
->list
);
383 /* Return the local path to the gpo */
384 *ret_local_path
= local_path
;
386 talloc_free(mem_ctx
);
390 static NTSTATUS
push_recursive (struct gp_context
*gp_ctx
, const char *local_path
,
391 const char *remote_path
, int depth
)
394 struct dirent
*dirent
;
395 char *entry_local_path
;
396 char *entry_remote_path
;
397 int local_fd
, remote_fd
;
399 int nread
, total_read
;
402 dir
= opendir(local_path
);
403 while ((dirent
= readdir(dir
)) != NULL
) {
404 if (strcmp(dirent
->d_name
, ".") == 0 ||
405 strcmp(dirent
->d_name
, "..") == 0) {
409 entry_local_path
= talloc_asprintf(gp_ctx
, "%s/%s", local_path
,
411 NT_STATUS_HAVE_NO_MEMORY(entry_local_path
);
413 entry_remote_path
= talloc_asprintf(gp_ctx
, "%s\\%s",
414 remote_path
, dirent
->d_name
);
415 NT_STATUS_HAVE_NO_MEMORY(entry_remote_path
);
417 if (stat(entry_local_path
, &s
) != 0) {
418 return NT_STATUS_UNSUCCESSFUL
;
420 if (s
.st_mode
& S_IFDIR
) {
421 DEBUG(6, ("Pushing directory %s to %s on sysvol\n",
422 entry_local_path
, entry_remote_path
));
423 smbcli_mkdir(gp_ctx
->cli
->tree
, entry_remote_path
);
424 if (depth
< GP_MAX_DEPTH
) {
425 push_recursive(gp_ctx
, entry_local_path
,
426 entry_remote_path
, depth
+1);
429 DEBUG(6, ("Pushing file %s to %s on sysvol\n",
430 entry_local_path
, entry_remote_path
));
431 remote_fd
= smbcli_open(gp_ctx
->cli
->tree
,
436 talloc_free(entry_local_path
);
437 talloc_free(entry_remote_path
);
438 DEBUG(0, ("Failed to create remote file: %s\n",
440 return NT_STATUS_UNSUCCESSFUL
;
442 local_fd
= open(entry_local_path
, O_RDONLY
);
444 talloc_free(entry_local_path
);
445 talloc_free(entry_remote_path
);
446 DEBUG(0, ("Failed to open local file: %s\n",
448 return NT_STATUS_UNSUCCESSFUL
;
451 while ((nread
= read(local_fd
, &buf
, sizeof(buf
)))) {
452 smbcli_write(gp_ctx
->cli
->tree
, remote_fd
, 0,
453 &buf
, total_read
, nread
);
458 smbcli_close(gp_ctx
->cli
->tree
, remote_fd
);
460 talloc_free(entry_local_path
);
461 talloc_free(entry_remote_path
);
470 NTSTATUS
gp_push_gpt(struct gp_context
*gp_ctx
, const char *local_path
,
471 const char *file_sys_path
)
476 if (gp_ctx
->cli
== NULL
) {
477 status
= gp_cli_connect(gp_ctx
);
478 if (!NT_STATUS_IS_OK(status
)) {
479 DEBUG(0, ("Failed to create cli connection to DC\n"));
483 share_path
= gp_get_share_path(gp_ctx
, file_sys_path
);
485 DEBUG(5, ("Copying %s to %s on sysvol\n", local_path
, share_path
));
487 smbcli_mkdir(gp_ctx
->cli
->tree
, share_path
);
489 status
= push_recursive(gp_ctx
, local_path
, share_path
, 0);
491 talloc_free(share_path
);
495 NTSTATUS
gp_create_gpt(struct gp_context
*gp_ctx
, const char *name
,
496 const char *file_sys_path
)
499 const char *tmp_dir
, *policy_dir
, *tmp_str
;
503 const char *file_content
= "[General]\r\nVersion=0\r\n";
505 /* Create a forked memory context, as a base for everything here */
506 mem_ctx
= talloc_new(gp_ctx
);
507 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
509 tmp_dir
= gp_tmpdir(mem_ctx
);
510 NT_STATUS_HAVE_NO_MEMORY(tmp_dir
);
511 policy_dir
= talloc_asprintf(mem_ctx
, "%s/%s", tmp_dir
, name
);
512 NT_STATUS_HAVE_NO_MEMORY(policy_dir
);
514 /* Create the directories */
516 rv
= mkdir(policy_dir
, 0755);
518 DEBUG(0, ("Could not create the policy dir: %s\n", policy_dir
));
519 talloc_free(mem_ctx
);
520 return NT_STATUS_UNSUCCESSFUL
;
523 tmp_str
= talloc_asprintf(mem_ctx
, "%s/User", policy_dir
);
524 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
525 rv
= mkdir(tmp_str
, 0755);
527 DEBUG(0, ("Could not create the User dir: %s\n", tmp_str
));
528 talloc_free(mem_ctx
);
529 return NT_STATUS_UNSUCCESSFUL
;
532 tmp_str
= talloc_asprintf(mem_ctx
, "%s/Machine", policy_dir
);
533 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
534 rv
= mkdir(tmp_str
, 0755);
536 DEBUG(0, ("Could not create the Machine dir: %s\n", tmp_str
));
537 talloc_free(mem_ctx
);
538 return NT_STATUS_UNSUCCESSFUL
;
541 /* Create a GPT.INI with version 0 */
543 tmp_str
= talloc_asprintf(mem_ctx
, "%s/GPT.INI", policy_dir
);
544 NT_STATUS_HAVE_NO_MEMORY(tmp_str
);
545 fd
= open(tmp_str
, O_CREAT
| O_WRONLY
, 0644);
547 DEBUG(0, ("Could not create the GPT.INI: %s\n", tmp_str
));
548 talloc_free(mem_ctx
);
549 return NT_STATUS_UNSUCCESSFUL
;
552 rv
= write(fd
, file_content
, strlen(file_content
));
553 if (rv
!= strlen(file_content
)) {
554 DEBUG(0, ("Short write in GPT.INI\n"));
555 talloc_free(mem_ctx
);
556 return NT_STATUS_UNSUCCESSFUL
;
561 /* Upload the GPT to the sysvol share on a DC */
562 status
= gp_push_gpt(gp_ctx
, policy_dir
, file_sys_path
);
563 if (!NT_STATUS_IS_OK(status
)) {
564 talloc_free(mem_ctx
);
568 talloc_free(mem_ctx
);
572 NTSTATUS
gp_set_gpt_security_descriptor(struct gp_context
*gp_ctx
,
573 struct gp_object
*gpo
,
574 struct security_descriptor
*sd
)
578 union smb_setfileinfo fileinfo
;
580 union smb_close io_close
;
582 /* Create a connection to sysvol if it is not already there */
583 if (gp_ctx
->cli
== NULL
) {
584 status
= gp_cli_connect(gp_ctx
);
585 if (!NT_STATUS_IS_OK(status
)) {
586 DEBUG(0, ("Failed to create cli connection to DC\n"));
591 /* Create a forked memory context which can be freed easily */
592 mem_ctx
= talloc_new(gp_ctx
);
593 NT_STATUS_HAVE_NO_MEMORY(mem_ctx
);
595 /* Open the directory with NTCreate AndX call */
596 io
.generic
.level
= RAW_OPEN_NTCREATEX
;
597 io
.ntcreatex
.in
.root_fid
.fnum
= 0;
598 io
.ntcreatex
.in
.flags
= 0;
599 io
.ntcreatex
.in
.access_mask
= SEC_FLAG_MAXIMUM_ALLOWED
;
600 io
.ntcreatex
.in
.create_options
= 0;
601 io
.ntcreatex
.in
.file_attr
= FILE_ATTRIBUTE_NORMAL
;
602 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
603 NTCREATEX_SHARE_ACCESS_WRITE
;
604 io
.ntcreatex
.in
.alloc_size
= 0;
605 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN
;
606 io
.ntcreatex
.in
.impersonation
= NTCREATEX_IMPERSONATION_ANONYMOUS
;
607 io
.ntcreatex
.in
.security_flags
= 0;
608 io
.ntcreatex
.in
.fname
= gp_get_share_path(mem_ctx
, gpo
->file_sys_path
);
609 status
= smb_raw_open(gp_ctx
->cli
->tree
, mem_ctx
, &io
);
610 if (!NT_STATUS_IS_OK(status
)) {
611 DEBUG(0, ("Can't open GPT directory\n"));
612 talloc_free(mem_ctx
);
616 /* Set the security descriptor on the directory */
617 fileinfo
.generic
.level
= RAW_FILEINFO_SEC_DESC
;
618 fileinfo
.set_secdesc
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
619 fileinfo
.set_secdesc
.in
.secinfo_flags
= SECINFO_PROTECTED_DACL
|
623 fileinfo
.set_secdesc
.in
.sd
= sd
;
624 status
= smb_raw_setfileinfo(gp_ctx
->cli
->tree
, &fileinfo
);
625 if (!NT_STATUS_IS_OK(status
)) {
626 DEBUG(0, ("Failed to set security descriptor on the GPT\n"));
627 talloc_free(mem_ctx
);
631 /* Close the directory */
632 io_close
.close
.level
= RAW_CLOSE_CLOSE
;
633 io_close
.close
.in
.file
.fnum
= io
.ntcreatex
.out
.file
.fnum
;
634 io_close
.close
.in
.write_time
= 0;
635 status
= smb_raw_close(gp_ctx
->cli
->tree
, &io_close
);
636 if (!NT_STATUS_IS_OK(status
)) {
637 DEBUG(0, ("Failed to close directory\n"));
638 talloc_free(mem_ctx
);
642 talloc_free(mem_ctx
);