cf667a64ebc93c2abf0a660bf58da031288b1e0b
[amitay/samba.git] / examples / winexe / winexe.c
1 /*
2  * Samba Unix/Linux CIFS implementation
3  *
4  * winexe
5  *
6  * Copyright (C) 2018 Volker Lendecke <vl@samba.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "includes.h"
23 #include <tevent.h>
24 #include <popt.h>
25 #include "version.h"
26 #include "lib/param/param.h"
27 #include "auth/credentials/credentials.h"
28 #include "lib/util/talloc_stack.h"
29 #include "lib/util/tevent_ntstatus.h"
30 #include "lib/util/sys_rw.h"
31 #include "libsmb/proto.h"
32 #include "librpc/gen_ndr/ndr_svcctl_c.h"
33 #include "rpc_client/cli_pipe.h"
34 #include "libcli/smb/smbXcli_base.h"
35 #include "libcli/util/werror.h"
36 #include "lib/async_req/async_sock.h"
37 #include "client.h"
38
39 #define SVC_INTERACTIVE 1
40 #define SVC_IGNORE_INTERACTIVE 2
41 #define SVC_INTERACTIVE_MASK 3
42 #define SVC_FORCE_UPLOAD 4
43 #define SVC_OS64BIT 8
44 #define SVC_OSCHOOSE 16
45 #define SVC_UNINSTALL 32
46 #define SVC_SYSTEM 64
47
48 #define SERVICE_NAME "winexesvc"
49
50 #define PIPE_NAME "ahexec"
51 #define PIPE_NAME_IN "ahexec_stdin%08X"
52 #define PIPE_NAME_OUT "ahexec_stdout%08X"
53 #define PIPE_NAME_ERR "ahexec_stderr%08X"
54
55 static const char version_message_fmt[] = "winexe version %d.%d\n"
56         "This program may be freely redistributed under the terms of the "
57         "GNU GPLv3\n";
58
59 struct program_options {
60         char *hostname;
61         char *cmd;
62         struct cli_credentials *credentials;
63         char *runas;
64         char *runas_file;
65         int flags;
66 };
67
68 static void parse_args(int argc, const char *argv[],
69                        TALLOC_CTX *mem_ctx,
70                        struct program_options *options,
71                        struct loadparm_context *lp_ctx)
72 {
73         poptContext pc;
74         int opt, i;
75         struct cli_credentials *cred;
76
77         int argc_new;
78         char **argv_new;
79
80         int flag_interactive = SVC_IGNORE_INTERACTIVE;
81         int flag_ostype = 2;
82         int flag_reinstall = 0;
83         int flag_uninstall = 0;
84         int flag_help = 0;
85         int flag_version = 0;
86         int flag_nopass = 0;
87         char *opt_user = NULL;
88         char *opt_kerberos = NULL;
89         char *opt_auth_file = NULL;
90         char *opt_debuglevel = NULL;
91
92         struct poptOption long_options[] = {
93                 { "help", 'h', POPT_ARG_NONE, &flag_help, 0,
94                   "Display help message" },
95                 { "version", 'V', POPT_ARG_NONE, &flag_version, 0,
96                   "Display version number" },
97                 { "user", 'U', POPT_ARG_STRING, &opt_user, 0,
98                   "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" },
99                 { "authentication-file", 'A',
100                   POPT_ARG_STRING, &opt_auth_file, 0,
101                   "Get the credentials from a file", "FILE" },
102                 { "no-pass", 'N', POPT_ARG_NONE, &flag_nopass, 0,
103                   "Do not ask for a password", NULL },
104                 { "kerberos", 'k', POPT_ARG_STRING, &opt_kerberos, 0,
105                   "Use Kerberos, -k [yes|no]" },
106                 { "debuglevel", 'd', POPT_ARG_STRING, &opt_debuglevel, 0,
107                   "Set debug level", "DEBUGLEVEL" },
108                 { "uninstall", 0, POPT_ARG_NONE, &flag_uninstall, 0,
109                   "Uninstall winexe service after remote execution", NULL},
110                 { "reinstall", 0, POPT_ARG_NONE, &flag_reinstall, 0,
111                   "Reinstall winexe service before remote execution", NULL},
112                 { "runas", 0, POPT_ARG_STRING, &options->runas, 0,
113                   "Run as the given user (BEWARE: this password is sent "
114                   "in cleartext over the network!)",
115                   "[DOMAIN\\]USERNAME%PASSWORD"},
116                 { "runas-file", 0, POPT_ARG_STRING, &options->runas_file, 0,
117                   "Run as user options defined in a file", "FILE"},
118                 { "interactive", 0, POPT_ARG_INT, &flag_interactive, 0,
119                   "Desktop interaction: 0 - disallow, 1 - allow. If allow, "
120                   "also use the --system switch (Windows requirement). Vista "
121                   "does not support this option.", "0|1"},
122                 { "ostype", 0, POPT_ARG_INT, &flag_ostype, 0,
123                   "OS type: 0 - 32-bit, 1 - 64-bit, 2 - winexe will decide. "
124                   "Determines which version (32-bit or 64-bit) of service "
125                   "will be installed.", "0|1|2"},
126                 POPT_TABLEEND
127         };
128
129         ZERO_STRUCTP(options);
130
131         pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,
132                             0);
133
134         poptSetOtherOptionHelp(pc, "[OPTION]... //HOST COMMAND\nOptions:");
135
136         if (((opt = poptGetNextOpt(pc)) != -1) || flag_help || flag_version) {
137                 fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
138                         SAMBA_VERSION_MINOR);
139                 if (flag_version) {
140                         exit(0);
141                 }
142                 poptPrintHelp(pc, stdout, 0);
143                 if (flag_help) {
144                         exit(0);
145                 }
146                 exit(1);
147         }
148
149         argv_new = discard_const_p(char *, poptGetArgs(pc));
150
151         argc_new = argc;
152         for (i = 0; i < argc; i++) {
153                 if (!argv_new || argv_new[i] == NULL) {
154                         argc_new = i;
155                         break;
156                 }
157         }
158
159         if (argc_new != 2 || argv_new[0][0] != '/' || argv_new[0][1] != '/') {
160                 fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
161                         SAMBA_VERSION_MINOR);
162                 poptPrintHelp(pc, stdout, 0);
163                 exit(1);
164         }
165
166         if (opt_debuglevel) {
167                 lp_set_cmdline("log level", opt_debuglevel);
168         }
169
170         cred = cli_credentials_init(mem_ctx);
171
172         if (opt_user) {
173                 cli_credentials_parse_string(cred, opt_user, CRED_SPECIFIED);
174         } else if (opt_auth_file) {
175                 cli_credentials_parse_file(cred, opt_auth_file,
176                                            CRED_SPECIFIED);
177         }
178
179         cli_credentials_guess(cred, lp_ctx);
180         if (!cli_credentials_get_password(cred) && !flag_nopass) {
181                 char *p = getpass("Enter password: ");
182                 if (*p) {
183                         cli_credentials_set_password(cred, p, CRED_SPECIFIED);
184                 }
185         }
186
187         if (opt_kerberos) {
188                 cli_credentials_set_kerberos_state(cred,
189                                                    strcmp(opt_kerberos, "yes")
190                                                    ? CRED_MUST_USE_KERBEROS
191                                                    : CRED_DONT_USE_KERBEROS);
192         }
193
194         if (options->runas == NULL && options->runas_file != NULL) {
195                 struct cli_credentials *runas_cred;
196                 const char *user;
197                 const char *pass;
198
199                 runas_cred = cli_credentials_init(mem_ctx);
200                 cli_credentials_parse_file(runas_cred, options->runas_file,
201                                            CRED_SPECIFIED);
202
203                 user = cli_credentials_get_username(runas_cred);
204                 pass = cli_credentials_get_password(runas_cred);
205
206                 if (user && pass) {
207                         char buffer[1024];
208                         const char *dom;
209
210                         dom = cli_credentials_get_domain(runas_cred);
211                         if (dom) {
212                                 snprintf(buffer, sizeof(buffer), "%s\\%s%%%s",
213                                          dom, user, pass);
214                         } else {
215                                 snprintf(buffer, sizeof(buffer), "%s%%%s",
216                                          user, pass);
217                         }
218                         buffer[sizeof(buffer)-1] = '\0';
219                         options->runas = talloc_strdup(mem_ctx, buffer);
220                 }
221         }
222
223         options->credentials = cred;
224
225         options->hostname = argv_new[0] + 2;
226         options->cmd = argv_new[1];
227
228         options->flags = flag_interactive;
229         if (flag_reinstall) {
230                 options->flags |= SVC_FORCE_UPLOAD;
231         }
232         if (flag_ostype == 1) {
233                 options->flags |= SVC_OS64BIT;
234         }
235         if (flag_ostype == 2) {
236                 options->flags |= SVC_OSCHOOSE;
237         }
238         if (flag_uninstall) {
239                 options->flags |= SVC_UNINSTALL;
240         }
241 }
242
243 static NTSTATUS winexe_svc_upload(
244         const char *hostname,
245         const char *service_filename,
246         const DATA_BLOB *svc32_exe,
247         const DATA_BLOB *svc64_exe,
248         struct cli_credentials *credentials,
249         int flags)
250 {
251         struct cli_state *cli;
252         uint16_t fnum;
253         NTSTATUS status;
254         const DATA_BLOB *binary = NULL;
255
256         status = cli_full_connection_creds(
257                 &cli,
258                 NULL,
259                 hostname,
260                 NULL,
261                 445,
262                 "ADMIN$",
263                 "?????",
264                 credentials,
265                 0,
266                 0);
267         if (!NT_STATUS_IS_OK(status)) {
268                 DBG_WARNING("cli_full_connection_creds failed: %s\n",
269                             nt_errstr(status));
270                 return status;
271         }
272
273         if (flags & SVC_FORCE_UPLOAD) {
274                 status = cli_unlink(cli, service_filename, 0);
275                 if (!NT_STATUS_IS_OK(status)) {
276                         DBG_WARNING("cli_unlink failed: %s\n",
277                                     nt_errstr(status));
278                 }
279         }
280
281         if (flags & SVC_OSCHOOSE) {
282                 status = cli_chkpath(cli, "SysWoW64");
283                 if (NT_STATUS_IS_OK(status)) {
284                         flags |= SVC_OS64BIT;
285                 }
286         }
287
288         if (flags & SVC_OS64BIT) {
289                 binary = svc64_exe;
290         } else {
291                 binary = svc32_exe;
292         }
293
294         if (binary == NULL) {
295                 //TODO
296         }
297
298         status = cli_ntcreate(
299                 cli,
300                 service_filename,
301                 0,                      /* CreatFlags */
302                 SEC_FILE_WRITE_DATA,    /* DesiredAccess */
303                 FILE_ATTRIBUTE_NORMAL,  /* FileAttributes */
304                 FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */
305                 FILE_OPEN_IF,            /* CreateDisposition */
306                 FILE_NON_DIRECTORY_FILE, /* CreateOptions */
307                 0,                       /* SecurityFlags */
308                 &fnum,
309                 NULL);          /* CreateReturns */
310         if (!NT_STATUS_IS_OK(status)) {
311                 DBG_WARNING("Could not create %s: %s\n", service_filename,
312                             nt_errstr(status));
313                 goto done;
314         }
315
316         status = cli_writeall(
317                 cli,
318                 fnum,
319                 0,
320                 binary->data,
321                 0,
322                 binary->length,
323                 NULL);
324         if (!NT_STATUS_IS_OK(status)) {
325                 DBG_WARNING("Could not write file: %s\n", nt_errstr(status));
326                 goto close_done;
327         }
328
329 close_done:
330         status = cli_close(cli, fnum);
331         if (!NT_STATUS_IS_OK(status)) {
332                 DBG_WARNING("Close(%"PRIu16") failed for %s: %s\n", fnum,
333                             service_filename, nt_errstr(status));
334         }
335 done:
336         TALLOC_FREE(cli);
337         return status;
338 }
339
340 static NTSTATUS winexe_svc_install(
341         struct cli_state *cli,
342         const char *hostname,
343         const char *service_name,
344         const char *service_filename,
345         const DATA_BLOB *svc32_exe,
346         const DATA_BLOB *svc64_exe,
347         struct cli_credentials *credentials,
348         int flags)
349 {
350         TALLOC_CTX *frame = talloc_stackframe();
351         struct rpc_pipe_client *rpccli;
352         struct policy_handle scmanager_handle;
353         struct policy_handle service_handle;
354         struct SERVICE_STATUS service_status;
355         bool need_start = false;
356         bool need_conf = false;
357         NTSTATUS status;
358         WERROR werr;
359
360         status = cli_rpc_pipe_open_noauth_transport(
361                 cli,
362                 NCACN_NP,
363                 &ndr_table_svcctl,
364                 &rpccli);
365         if (!NT_STATUS_IS_OK(status)) {
366                 DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
367                             nt_errstr(status));
368                 goto done;
369         }
370
371         status = dcerpc_svcctl_OpenSCManagerW(
372                 rpccli->binding_handle,
373                 frame,
374                 smbXcli_conn_remote_name(cli->conn),
375                 NULL,
376                 SEC_FLAG_MAXIMUM_ALLOWED,
377                 &scmanager_handle,
378                 &werr);
379         if (!NT_STATUS_IS_OK(status)) {
380                 DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
381                             nt_errstr(status));
382                 goto done;
383         }
384         if (!W_ERROR_IS_OK(werr)) {
385                 DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
386                             win_errstr(werr));
387                 goto done;
388         }
389
390         status = dcerpc_svcctl_OpenServiceW(
391                 rpccli->binding_handle,
392                 frame,
393                 &scmanager_handle,
394                 service_name,
395                 SERVICE_ALL_ACCESS,
396                 &service_handle,
397                 &werr);
398         if (!NT_STATUS_IS_OK(status)) {
399                 DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
400                             nt_errstr(status));
401                 goto close_scmanager;
402         }
403
404         if (W_ERROR_EQUAL(werr,  WERR_SERVICE_DOES_NOT_EXIST)) {
405                 status = dcerpc_svcctl_CreateServiceW(
406                         rpccli->binding_handle,
407                         frame,
408                         &scmanager_handle,
409                         service_name,
410                         NULL,
411                         SERVICE_ALL_ACCESS,
412                         SERVICE_TYPE_WIN32_OWN_PROCESS |
413                         ((flags & SVC_INTERACTIVE) ?
414                          SERVICE_TYPE_INTERACTIVE_PROCESS : 0),
415                         SVCCTL_DEMAND_START,
416                         SVCCTL_SVC_ERROR_NORMAL,
417                         service_filename,
418                         NULL,
419                         NULL,
420                         NULL,
421                         0,
422                         NULL,
423                         NULL,
424                         0,
425                         &service_handle,
426                         &werr);
427                 if (!NT_STATUS_IS_OK(status)) {
428                         DBG_WARNING("dcerpc_svcctl_CreateServiceW "
429                                     "failed: %s\n", nt_errstr(status));
430                         goto close_scmanager;
431                 }
432                 if (!W_ERROR_IS_OK(werr)) {
433                         DBG_WARNING("dcerpc_svcctl_CreateServiceW "
434                                     "failed: %s\n", win_errstr(werr));
435                         status = werror_to_ntstatus(werr);
436                         goto close_scmanager;
437                 }
438         }
439
440         status = dcerpc_svcctl_QueryServiceStatus(
441                 rpccli->binding_handle,
442                 frame,
443                 &service_handle,
444                 &service_status,
445                 &werr);
446
447         if (!NT_STATUS_IS_OK(status)) {
448                 DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
449                             "failed: %s\n", nt_errstr(status));
450                 goto close_service;
451         }
452         if (!W_ERROR_IS_OK(werr)) {
453                 DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
454                             "failed: %s\n", win_errstr(werr));
455                 status = werror_to_ntstatus(werr);
456                 goto close_service;
457         }
458
459         if (!(flags & SVC_IGNORE_INTERACTIVE)) {
460                 need_conf =
461                         !(service_status.type &
462                           SERVICE_TYPE_INTERACTIVE_PROCESS) ^
463                         !(flags & SVC_INTERACTIVE);
464         }
465
466         if (service_status.state == SVCCTL_STOPPED) {
467                 need_start = true;
468         } else if (need_conf) {
469                 status = dcerpc_svcctl_ControlService(
470                         rpccli->binding_handle,
471                         frame,
472                         &service_handle,
473                         SVCCTL_CONTROL_STOP,
474                         &service_status,
475                         &werr);
476
477                 if (!NT_STATUS_IS_OK(status)) {
478                         DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
479                             "failed: %s\n", nt_errstr(status));
480                         goto close_service;
481                 }
482                 if (!W_ERROR_IS_OK(werr)) {
483                         DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
484                                     "failed: %s\n", win_errstr(werr));
485                         status = werror_to_ntstatus(werr);
486                         goto close_service;
487                 }
488
489                 do {
490                         smb_msleep(100);
491
492                         status = dcerpc_svcctl_QueryServiceStatus(
493                                 rpccli->binding_handle,
494                                 frame,
495                                 &service_handle,
496                                 &service_status,
497                                 &werr);
498
499                         if (!NT_STATUS_IS_OK(status)) {
500                                 DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
501                                             "failed: %s\n", nt_errstr(status));
502                                 goto close_service;
503                         }
504                         if (!W_ERROR_IS_OK(werr)) {
505                                 DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
506                                             "failed: %s\n", win_errstr(werr));
507                                 status = werror_to_ntstatus(werr);
508                                 goto close_service;
509                         }
510                 } while (service_status.state == SVCCTL_STOP_PENDING);
511
512                 need_start = 1;
513         }
514
515         if (need_conf) {
516                 status = dcerpc_svcctl_ChangeServiceConfigW(
517                         rpccli->binding_handle,
518                         frame,
519                         &service_handle,
520                         SERVICE_TYPE_WIN32_OWN_PROCESS |
521                         ((flags & SVC_INTERACTIVE) ?
522                          SERVICE_TYPE_INTERACTIVE_PROCESS : 0), /* type */
523                         UINT32_MAX, /* start_type, SERVICE_NO_CHANGE */
524                         UINT32_MAX, /* error_control, SERVICE_NO_CHANGE */
525                         NULL,       /* binary_path */
526                         NULL,       /* load_order_group */
527                         NULL,       /* tag_id */
528                         NULL,       /* dependencies */
529                         NULL,       /* service_start_name */
530                         NULL,       /* password */
531                         NULL,       /* display_name */
532                         &werr);
533
534                 if (!NT_STATUS_IS_OK(status)) {
535                         DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
536                                     "failed: %s\n", nt_errstr(status));
537                         goto close_service;
538                 }
539                 if (!W_ERROR_IS_OK(werr)) {
540                         DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
541                                     "failed: %s\n", win_errstr(werr));
542                         status = werror_to_ntstatus(werr);
543                         goto close_service;
544                 }
545         }
546
547         if (need_start) {
548                 status = winexe_svc_upload(
549                         hostname,
550                         service_filename,
551                         svc32_exe,
552                         svc64_exe,
553                         credentials,
554                         flags);
555                 if (!NT_STATUS_IS_OK(status)) {
556                         DBG_WARNING("winexe_svc_upload failed: %s\n",
557                                     nt_errstr(status));
558                         goto close_service;
559                 }
560
561                 status = dcerpc_svcctl_StartServiceW(
562                         rpccli->binding_handle,
563                         frame,
564                         &service_handle,
565                         0,      /* num_args */
566                         NULL,   /* arguments */
567                         &werr);
568
569                 if (!NT_STATUS_IS_OK(status)) {
570                         DBG_WARNING("dcerpc_svcctl_StartServiceW "
571                                     "failed: %s\n", nt_errstr(status));
572                         goto close_service;
573                 }
574                 if (!W_ERROR_IS_OK(werr)) {
575                         DBG_WARNING("dcerpc_svcctl_StartServiceW "
576                                     "failed: %s\n", win_errstr(werr));
577                         status = werror_to_ntstatus(werr);
578                         goto close_service;
579                 }
580
581                 do {
582                         smb_msleep(100);
583
584                         status = dcerpc_svcctl_QueryServiceStatus(
585                                 rpccli->binding_handle,
586                                 frame,
587                                 &service_handle,
588                                 &service_status,
589                                 &werr);
590
591                         if (!NT_STATUS_IS_OK(status)) {
592                                 DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
593                                             "failed: %s\n", nt_errstr(status));
594                                 goto close_service;
595                         }
596                         if (!W_ERROR_IS_OK(werr)) {
597                                 DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
598                                             "failed: %s\n", win_errstr(werr));
599                                 status = werror_to_ntstatus(werr);
600                                 goto close_service;
601                         }
602                 } while (service_status.state == SVCCTL_START_PENDING);
603
604                 if (service_status.state != SVCCTL_RUNNING) {
605                         DBG_WARNING("Failed to start service\n");
606                         status = NT_STATUS_UNSUCCESSFUL;
607                         goto close_service;
608                 }
609         }
610
611 close_service:
612         {
613                 NTSTATUS close_status;
614                 WERROR close_werr;
615
616                 close_status = dcerpc_svcctl_CloseServiceHandle(
617                         rpccli->binding_handle,
618                         frame,
619                         &service_handle,
620                         &close_werr);
621                 if (!NT_STATUS_IS_OK(close_status)) {
622                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
623                                     "failed: %s\n", nt_errstr(close_status));
624                         goto done;
625                 }
626                 if (!W_ERROR_IS_OK(close_werr)) {
627                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
628                                     " failed: %s\n", win_errstr(close_werr));
629                         goto done;
630                 }
631         }
632
633 close_scmanager:
634         {
635                 NTSTATUS close_status;
636                 WERROR close_werr;
637
638                 close_status = dcerpc_svcctl_CloseServiceHandle(
639                         rpccli->binding_handle,
640                         frame,
641                         &scmanager_handle,
642                         &close_werr);
643                 if (!NT_STATUS_IS_OK(close_status)) {
644                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
645                                     "failed: %s\n", nt_errstr(close_status));
646                         goto done;
647                 }
648                 if (!W_ERROR_IS_OK(close_werr)) {
649                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
650                                     " failed: %s\n", win_errstr(close_werr));
651                         goto done;
652                 }
653         }
654
655 done:
656         TALLOC_FREE(rpccli);
657         TALLOC_FREE(frame);
658         return status;
659 }
660
661 static NTSTATUS winexe_svc_uninstall(
662         struct cli_state *cli,
663         const char *service_name)
664 {
665         TALLOC_CTX *frame = talloc_stackframe();
666         struct rpc_pipe_client *rpccli;
667         struct policy_handle scmanager_handle;
668         struct policy_handle service_handle;
669         struct SERVICE_STATUS service_status;
670         NTSTATUS status;
671         WERROR werr;
672
673         status = cli_rpc_pipe_open_noauth_transport(
674                 cli,
675                 NCACN_NP,
676                 &ndr_table_svcctl,
677                 &rpccli);
678         if (!NT_STATUS_IS_OK(status)) {
679                 DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
680                             nt_errstr(status));
681                 goto done;
682         }
683
684         status = dcerpc_svcctl_OpenSCManagerW(
685                 rpccli->binding_handle,
686                 frame,
687                 smbXcli_conn_remote_name(cli->conn),
688                 NULL,
689                 SEC_FLAG_MAXIMUM_ALLOWED,
690                 &scmanager_handle,
691                 &werr);
692         if (!NT_STATUS_IS_OK(status)) {
693                 DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
694                             nt_errstr(status));
695                 goto done;
696         }
697         if (!W_ERROR_IS_OK(werr)) {
698                 DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
699                             win_errstr(werr));
700                 goto done;
701         }
702
703         status = dcerpc_svcctl_OpenServiceW(
704                 rpccli->binding_handle,
705                 frame,
706                 &scmanager_handle,
707                 service_name,
708                 SERVICE_ALL_ACCESS,
709                 &service_handle,
710                 &werr);
711         if (!NT_STATUS_IS_OK(status)) {
712                 DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
713                             nt_errstr(status));
714                 goto close_scmanager;
715         }
716         if (!W_ERROR_IS_OK(werr)) {
717                 DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
718                             win_errstr(werr));
719                 status = werror_to_ntstatus(werr);
720                 goto close_scmanager;
721         }
722
723         status = dcerpc_svcctl_ControlService(
724                 rpccli->binding_handle,
725                 frame,
726                 &service_handle,
727                 SVCCTL_CONTROL_STOP,
728                 &service_status,
729                 &werr);
730         if (!NT_STATUS_IS_OK(status)) {
731                 DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
732                             "failed: %s\n", nt_errstr(status));
733                 goto close_service;
734         }
735         if (!W_ERROR_IS_OK(werr)) {
736                 DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
737                             "failed: %s\n", win_errstr(werr));
738                 status = werror_to_ntstatus(werr);
739                 goto close_service;
740         }
741
742         do {
743                 smb_msleep(100);
744
745                 status = dcerpc_svcctl_QueryServiceStatus(
746                         rpccli->binding_handle,
747                         frame,
748                         &service_handle,
749                         &service_status,
750                         &werr);
751
752                 if (!NT_STATUS_IS_OK(status)) {
753                         DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
754                                     "failed: %s\n", nt_errstr(status));
755                         goto close_service;
756                 }
757                 if (!W_ERROR_IS_OK(werr)) {
758                         DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
759                                     "failed: %s\n", win_errstr(werr));
760                         status = werror_to_ntstatus(werr);
761                         goto close_service;
762                 }
763         } while (service_status.state != SVCCTL_STOPPED);
764
765         status = dcerpc_svcctl_DeleteService(
766                 rpccli->binding_handle,
767                 frame,
768                 &service_handle,
769                 &werr);
770         if (!NT_STATUS_IS_OK(status)) {
771                 DBG_WARNING("dcerpc_svcctl_DeleteService "
772                             "failed: %s\n", nt_errstr(status));
773                 goto close_service;
774         }
775         if (!W_ERROR_IS_OK(werr)) {
776                 DBG_WARNING("dcerpc_svcctl_DeleteService "
777                             "failed: %s\n", win_errstr(werr));
778                 status = werror_to_ntstatus(werr);
779                 goto close_service;
780         }
781
782 close_service:
783         {
784                 NTSTATUS close_status;
785                 WERROR close_werr;
786
787                 close_status = dcerpc_svcctl_CloseServiceHandle(
788                         rpccli->binding_handle,
789                         frame,
790                         &service_handle,
791                         &close_werr);
792                 if (!NT_STATUS_IS_OK(close_status)) {
793                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
794                                     "failed: %s\n", nt_errstr(close_status));
795                         goto done;
796                 }
797                 if (!W_ERROR_IS_OK(close_werr)) {
798                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
799                                     " failed: %s\n", win_errstr(close_werr));
800                         goto done;
801                 }
802         }
803
804 close_scmanager:
805         {
806                 NTSTATUS close_status;
807                 WERROR close_werr;
808
809                 close_status = dcerpc_svcctl_CloseServiceHandle(
810                         rpccli->binding_handle,
811                         frame,
812                         &scmanager_handle,
813                         &close_werr);
814                 if (!NT_STATUS_IS_OK(close_status)) {
815                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
816                                     "failed: %s\n", nt_errstr(close_status));
817                         goto done;
818                 }
819                 if (!W_ERROR_IS_OK(close_werr)) {
820                         DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
821                                     " failed: %s\n", win_errstr(close_werr));
822                         goto done;
823                 }
824         }
825
826 done:
827         TALLOC_FREE(rpccli);
828         TALLOC_FREE(frame);
829         return status;
830 }
831
832 struct winexe_out_pipe_state {
833         struct tevent_context *ev;
834         struct cli_state *cli;
835         uint16_t out_pipe;
836         int out_fd;
837         char out_inbuf[256];
838 };
839
840 static void winexe_out_pipe_opened(struct tevent_req *subreq);
841 static void winexe_out_pipe_got_data(struct tevent_req *subreq);
842 static void winexe_out_pipe_closed(struct tevent_req *subreq);
843
844 static struct tevent_req *winexe_out_pipe_send(
845         TALLOC_CTX *mem_ctx,
846         struct tevent_context *ev,
847         struct cli_state *cli,
848         const char *pipe_name,
849         int out_fd)
850 {
851         struct tevent_req *req, *subreq;
852         struct winexe_out_pipe_state *state;
853
854         req = tevent_req_create(mem_ctx, &state,
855                                 struct winexe_out_pipe_state);
856         if (req == NULL) {
857                 return NULL;
858         }
859         state->ev = ev;
860         state->cli = cli;
861         state->out_fd = out_fd;
862
863         subreq = cli_ntcreate_send(
864                 state,
865                 state->ev,
866                 state->cli,
867                 pipe_name,
868                 0,
869                 SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
870                 SEC_RIGHTS_FILE_EXECUTE,
871                 0,              /* FileAttributes */
872                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
873                 FILE_OPEN,      /* CreateDisposition */
874                 0,              /* CreateOptions */
875                 0);             /* SecurityFlags */
876         if (tevent_req_nomem(subreq, req)) {
877                 return tevent_req_post(req, ev);
878         }
879         tevent_req_set_callback(subreq, winexe_out_pipe_opened, req);
880         return req;
881 }
882
883 static void winexe_out_pipe_opened(struct tevent_req *subreq)
884 {
885         struct tevent_req *req = tevent_req_callback_data(
886                 subreq, struct tevent_req);
887         struct winexe_out_pipe_state *state = tevent_req_data(
888                 req, struct winexe_out_pipe_state);
889         int timeout;
890         NTSTATUS status;
891
892         status = cli_ntcreate_recv(subreq, &state->out_pipe, NULL);
893         TALLOC_FREE(subreq);
894         if (tevent_req_nterror(req, status)) {
895                 return;
896         }
897
898         timeout = state->cli->timeout;
899         state->cli->timeout = 0;
900
901         subreq = cli_read_send(
902                 state,
903                 state->ev,
904                 state->cli,
905                 state->out_pipe,
906                 state->out_inbuf,
907                 0,
908                 sizeof(state->out_inbuf));
909
910         state->cli->timeout = timeout;
911
912         if (tevent_req_nomem(subreq, req)) {
913                 return;
914         }
915         tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
916 }
917
918 static void winexe_out_pipe_got_data(struct tevent_req *subreq)
919 {
920         struct tevent_req *req = tevent_req_callback_data(
921                 subreq, struct tevent_req);
922         struct winexe_out_pipe_state *state = tevent_req_data(
923                 req, struct winexe_out_pipe_state);
924         NTSTATUS status;
925         int timeout;
926         size_t received;
927         ssize_t written;
928
929         status = cli_read_recv(subreq, &received);
930         TALLOC_FREE(subreq);
931
932         DBG_DEBUG("cli_read for %d gave %s\n",
933                   state->out_fd,
934                   nt_errstr(status));
935
936         if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
937                 subreq = cli_close_send(
938                         state,
939                         state->ev,
940                         state->cli,
941                         state->out_pipe);
942                 if (tevent_req_nomem(subreq, req)) {
943                         return;
944                 }
945                 tevent_req_set_callback(subreq, winexe_out_pipe_closed, req);
946                 return;
947         }
948
949         if (tevent_req_nterror(req, status)) {
950                 return;
951         }
952
953         if (received > 0) {
954                 written = sys_write(state->out_fd, state->out_inbuf, received);
955                 if (written == -1) {
956                         tevent_req_nterror(req, map_nt_error_from_unix(errno));
957                         return;
958                 }
959         }
960
961         timeout = state->cli->timeout;
962         state->cli->timeout = 0;
963
964         subreq = cli_read_send(
965                 state,
966                 state->ev,
967                 state->cli,
968                 state->out_pipe,
969                 state->out_inbuf,
970                 0,
971                 sizeof(state->out_inbuf));
972
973         state->cli->timeout = timeout;
974
975         if (tevent_req_nomem(subreq, req)) {
976                 return;
977         }
978         tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
979 }
980
981 static void winexe_out_pipe_closed(struct tevent_req *subreq)
982 {
983         struct tevent_req *req = tevent_req_callback_data(
984                 subreq, struct tevent_req);
985         NTSTATUS status;
986
987         status = cli_close_recv(subreq);
988         TALLOC_FREE(subreq);
989         if (tevent_req_nterror(req, status)) {
990                 return;
991         }
992         tevent_req_done(req);
993 }
994
995 static NTSTATUS winexe_out_pipe_recv(struct tevent_req *req)
996 {
997         return tevent_req_simple_recv_ntstatus(req);
998 }
999
1000 struct winexe_in_pipe_state {
1001         struct tevent_context *ev;
1002         struct cli_state *cli;
1003         struct tevent_req *fd_read_req;
1004         bool close_requested;
1005         bool closing;
1006         uint16_t in_pipe;
1007         int in_fd;
1008         char inbuf[256];
1009 };
1010
1011 static void winexe_in_pipe_opened(struct tevent_req *subreq);
1012 static void winexe_in_pipe_got_data(struct tevent_req *subreq);
1013 static void winexe_in_pipe_written(struct tevent_req *subreq);
1014 static void winexe_in_pipe_closed(struct tevent_req *subreq);
1015
1016 static struct tevent_req *winexe_in_pipe_send(
1017         TALLOC_CTX *mem_ctx,
1018         struct tevent_context *ev,
1019         struct cli_state *cli,
1020         const char *pipe_name,
1021         int in_fd)
1022 {
1023         struct tevent_req *req, *subreq;
1024         struct winexe_in_pipe_state *state;
1025
1026         req = tevent_req_create(mem_ctx, &state,
1027                                 struct winexe_in_pipe_state);
1028         if (req == NULL) {
1029                 return NULL;
1030         }
1031         state->ev = ev;
1032         state->cli = cli;
1033         state->in_fd = in_fd;
1034
1035         subreq = cli_ntcreate_send(
1036                 state,
1037                 state->ev,
1038                 state->cli,
1039                 pipe_name,
1040                 0,
1041                 SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
1042                 SEC_RIGHTS_FILE_EXECUTE,
1043                 0,              /* FileAttributes */
1044                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1045                 FILE_OPEN,      /* CreateDisposition */
1046                 0,              /* CreateOptions */
1047                 0);             /* SecurityFlags */
1048         if (tevent_req_nomem(subreq, req)) {
1049                 return tevent_req_post(req, ev);
1050         }
1051         tevent_req_set_callback(subreq, winexe_in_pipe_opened, req);
1052         return req;
1053 }
1054
1055 static void winexe_in_pipe_opened(struct tevent_req *subreq)
1056 {
1057         struct tevent_req *req = tevent_req_callback_data(
1058                 subreq, struct tevent_req);
1059         struct winexe_in_pipe_state *state = tevent_req_data(
1060                 req, struct winexe_in_pipe_state);
1061         NTSTATUS status;
1062
1063         status = cli_ntcreate_recv(subreq, &state->in_pipe, NULL);
1064         TALLOC_FREE(subreq);
1065         if (tevent_req_nterror(req, status)) {
1066                 return;
1067         }
1068
1069         subreq = wait_for_read_send(
1070                 state,
1071                 state->ev,
1072                 state->in_fd,
1073                 true);
1074         if (tevent_req_nomem(subreq, req)) {
1075                 return;
1076         }
1077         tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req);
1078
1079         state->fd_read_req = subreq;
1080 }
1081
1082 static void winexe_in_pipe_got_data(struct tevent_req *subreq)
1083 {
1084         struct tevent_req *req = tevent_req_callback_data(
1085                 subreq, struct tevent_req);
1086         struct winexe_in_pipe_state *state = tevent_req_data(
1087                 req, struct winexe_in_pipe_state);
1088         int err;
1089         bool ok;
1090         int timeout;
1091         ssize_t nread;
1092
1093         ok = wait_for_read_recv(subreq, &err);
1094         TALLOC_FREE(subreq);
1095         if (!ok) {
1096                 tevent_req_nterror(req, map_nt_error_from_unix(err));
1097                 return;
1098         }
1099         state->fd_read_req = NULL;
1100
1101         nread = sys_read(state->in_fd, &state->inbuf, sizeof(state->inbuf));
1102         if (nread == -1) {
1103                 tevent_req_nterror(req, map_nt_error_from_unix(errno));
1104                 return;
1105         }
1106         if (nread == 0) {
1107                 tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
1108                 return;
1109         }
1110
1111         timeout = state->cli->timeout;
1112         state->cli->timeout = 0;
1113
1114         subreq = cli_writeall_send(
1115                 state,
1116                 state->ev,
1117                 state->cli,
1118                 state->in_pipe,
1119                 0,
1120                 (uint8_t *)state->inbuf,
1121                 0,
1122                 nread);
1123
1124         state->cli->timeout = timeout;
1125
1126         if (tevent_req_nomem(subreq, req)) {
1127                 return;
1128         }
1129         tevent_req_set_callback(subreq, winexe_in_pipe_written, req);
1130 }
1131
1132 static void winexe_in_pipe_written(struct tevent_req *subreq)
1133 {
1134         struct tevent_req *req = tevent_req_callback_data(
1135                 subreq, struct tevent_req);
1136         struct winexe_in_pipe_state *state = tevent_req_data(
1137                 req, struct winexe_in_pipe_state);
1138         NTSTATUS status;
1139
1140         status = cli_writeall_recv(subreq, NULL);
1141         TALLOC_FREE(subreq);
1142
1143         DBG_DEBUG("cli_writeall for %d gave %s\n",
1144                   state->in_fd,
1145                   nt_errstr(status));
1146
1147         if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED) ||
1148             state->close_requested) {
1149                 subreq = cli_close_send(
1150                         state,
1151                         state->ev,
1152                         state->cli,
1153                         state->in_pipe);
1154                 if (tevent_req_nomem(subreq, req)) {
1155                         return;
1156                 }
1157                 tevent_req_set_callback(subreq, winexe_in_pipe_closed, req);
1158                 state->closing = true;
1159                 return;
1160         }
1161
1162         if (tevent_req_nterror(req, status)) {
1163                 return;
1164         }
1165
1166         subreq = wait_for_read_send(
1167                 state,
1168                 state->ev,
1169                 state->in_fd,
1170                 true);
1171         if (tevent_req_nomem(subreq, req)) {
1172                 return;
1173         }
1174         tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req);
1175
1176         state->fd_read_req = subreq;
1177 }
1178
1179 static void winexe_in_pipe_closed(struct tevent_req *subreq)
1180 {
1181         struct tevent_req *req = tevent_req_callback_data(
1182                 subreq, struct tevent_req);
1183         NTSTATUS status;
1184
1185         status = cli_close_recv(subreq);
1186         TALLOC_FREE(subreq);
1187         if (tevent_req_nterror(req, status)) {
1188                 return;
1189         }
1190         return tevent_req_done(req);
1191 }
1192
1193 static NTSTATUS winexe_in_pipe_recv(struct tevent_req *req)
1194 {
1195         return tevent_req_simple_recv_ntstatus(req);
1196 }
1197
1198 static bool winexe_in_pipe_close(struct tevent_req *req)
1199 {
1200         struct winexe_in_pipe_state *state = tevent_req_data(
1201                 req, struct winexe_in_pipe_state);
1202         struct tevent_req *subreq;
1203
1204         if (state->closing) {
1205                 return true;
1206         }
1207
1208         if (state->fd_read_req == NULL) {
1209                 /*
1210                  * cli_writeall active, wait for it to return
1211                  */
1212                 state->close_requested = true;
1213                 return true;
1214         }
1215
1216         TALLOC_FREE(state->fd_read_req);
1217
1218         subreq = cli_close_send(
1219                 state,
1220                 state->ev,
1221                 state->cli,
1222                 state->in_pipe);
1223         if (subreq == NULL) {
1224                 return false;
1225         }
1226         tevent_req_set_callback(subreq, winexe_in_pipe_closed, req);
1227         state->closing = true;
1228
1229         return true;
1230 }
1231
1232 struct winexe_pipes_state {
1233         struct tevent_req *pipes[3];
1234 };
1235
1236 static void winexe_pipes_stdin_done(struct tevent_req *subreq);
1237 static void winexe_pipes_stdout_done(struct tevent_req *subreq);
1238 static void winexe_pipes_stderr_done(struct tevent_req *subreq);
1239
1240 static struct tevent_req *winexe_pipes_send(
1241         TALLOC_CTX *mem_ctx,
1242         struct tevent_context *ev,
1243         struct cli_state *cli,
1244         const char *pipe_postfix)
1245 {
1246         struct tevent_req *req;
1247         struct winexe_pipes_state *state;
1248         char *pipe_name;
1249
1250         req = tevent_req_create(mem_ctx, &state, struct winexe_pipes_state);
1251         if (req == NULL) {
1252                 return NULL;
1253         }
1254
1255         pipe_name = talloc_asprintf(state, "\\ahexec_stdin%s", pipe_postfix);
1256         if (tevent_req_nomem(pipe_name, req)) {
1257                 return tevent_req_post(req, ev);
1258         }
1259         state->pipes[0] = winexe_in_pipe_send(
1260                 state,
1261                 ev,
1262                 cli,
1263                 pipe_name,
1264                 0);
1265         if (tevent_req_nomem(state->pipes[0], req)) {
1266                 return tevent_req_post(req, ev);
1267         }
1268         tevent_req_set_callback(state->pipes[0], winexe_pipes_stdin_done, req);
1269
1270         pipe_name = talloc_asprintf(state, "\\ahexec_stdout%s", pipe_postfix);
1271         if (tevent_req_nomem(pipe_name, req)) {
1272                 return tevent_req_post(req, ev);
1273         }
1274         state->pipes[1] = winexe_out_pipe_send(
1275                 state,
1276                 ev,
1277                 cli,
1278                 pipe_name,
1279                 1);
1280         if (tevent_req_nomem(state->pipes[1], req)) {
1281                 return tevent_req_post(req, ev);
1282         }
1283         tevent_req_set_callback(state->pipes[1], winexe_pipes_stdout_done,
1284                                 req);
1285
1286         pipe_name = talloc_asprintf(state, "\\ahexec_stderr%s", pipe_postfix);
1287         if (tevent_req_nomem(pipe_name, req)) {
1288                 return tevent_req_post(req, ev);
1289         }
1290         state->pipes[2] = winexe_out_pipe_send(
1291                 state,
1292                 ev,
1293                 cli,
1294                 pipe_name,
1295                 2);
1296         if (tevent_req_nomem(state->pipes[2], req)) {
1297                 return tevent_req_post(req, ev);
1298         }
1299         tevent_req_set_callback(state->pipes[2], winexe_pipes_stderr_done,
1300                                 req);
1301
1302         DBG_DEBUG("pipes = %p %p %p\n",
1303                   state->pipes[0],
1304                   state->pipes[1],
1305                   state->pipes[2]);
1306
1307         return req;
1308 }
1309
1310 static void winexe_pipes_stdin_done(struct tevent_req *subreq)
1311 {
1312         struct tevent_req *req = tevent_req_callback_data(
1313                 subreq, struct tevent_req);
1314         struct winexe_pipes_state *state = tevent_req_data(
1315                 req, struct winexe_pipes_state);
1316         NTSTATUS status;
1317
1318         status = winexe_in_pipe_recv(subreq);
1319         TALLOC_FREE(subreq);
1320
1321         DBG_DEBUG("stdin returned %s\n", nt_errstr(status));
1322
1323         if (tevent_req_nterror(req, status)) {
1324                 return;
1325         }
1326
1327         state->pipes[0] = NULL;
1328
1329         DBG_DEBUG("pipes = %p %p %p\n",
1330                   state->pipes[0],
1331                   state->pipes[1],
1332                   state->pipes[2]);
1333
1334         if ((state->pipes[1] == NULL) && (state->pipes[2] == NULL)) {
1335                 tevent_req_done(req);
1336         }
1337 }
1338
1339 static void winexe_pipes_stdout_done(struct tevent_req *subreq)
1340 {
1341         struct tevent_req *req = tevent_req_callback_data(
1342                 subreq, struct tevent_req);
1343         struct winexe_pipes_state *state = tevent_req_data(
1344                 req, struct winexe_pipes_state);
1345         NTSTATUS status;
1346
1347         status = winexe_out_pipe_recv(subreq);
1348         TALLOC_FREE(subreq);
1349
1350         DBG_DEBUG("stdout returned %s\n", nt_errstr(status));
1351
1352         if (tevent_req_nterror(req, status)) {
1353                 return;
1354         }
1355
1356         if (state->pipes[0] != NULL) {
1357                 winexe_in_pipe_close(state->pipes[0]);
1358         }
1359
1360         state->pipes[1] = NULL;
1361
1362         DBG_DEBUG("pipes = %p %p %p\n",
1363                   state->pipes[0],
1364                   state->pipes[1],
1365                   state->pipes[2]);
1366
1367         if ((state->pipes[0] == NULL) && (state->pipes[2] == NULL)) {
1368                 tevent_req_done(req);
1369         }
1370 }
1371
1372 static void winexe_pipes_stderr_done(struct tevent_req *subreq)
1373 {
1374         struct tevent_req *req = tevent_req_callback_data(
1375                 subreq, struct tevent_req);
1376         struct winexe_pipes_state *state = tevent_req_data(
1377                 req, struct winexe_pipes_state);
1378         NTSTATUS status;
1379
1380         status = winexe_out_pipe_recv(subreq);
1381         TALLOC_FREE(subreq);
1382
1383         DBG_DEBUG("stderr returned %s\n", nt_errstr(status));
1384
1385         if (tevent_req_nterror(req, status)) {
1386                 return;
1387         }
1388
1389         if (state->pipes[0] != NULL) {
1390                 winexe_in_pipe_close(state->pipes[0]);
1391         }
1392
1393         state->pipes[2] = NULL;
1394
1395         DBG_DEBUG("pipes = %p %p %p\n",
1396                   state->pipes[0],
1397                   state->pipes[1],
1398                   state->pipes[2]);
1399
1400         if ((state->pipes[0] == NULL) && (state->pipes[1] == NULL)) {
1401                 tevent_req_done(req);
1402         }
1403 }
1404
1405 static NTSTATUS winexe_pipes_recv(struct tevent_req *req)
1406 {
1407         return tevent_req_simple_recv_ntstatus(req);
1408 }
1409
1410 struct winexe_ctrl_state {
1411         struct tevent_context *ev;
1412         struct cli_state *cli;
1413
1414         uint16_t ctrl_pipe;
1415         bool ctrl_pipe_done;
1416
1417         char ctrl_inbuf[256];
1418         char *cmd;
1419         int return_code;
1420
1421         struct tevent_req *pipes_req;
1422 };
1423
1424 static void winexe_ctrl_opened(struct tevent_req *subreq);
1425 static void winexe_ctrl_got_read(struct tevent_req *subreq);
1426 static void winexe_ctrl_wrote_version(struct tevent_req *subreq);
1427 static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq);
1428 static void winexe_ctrl_pipes_done(struct tevent_req *subreq);
1429 static void winexe_ctrl_pipe_closed(struct tevent_req *subreq);
1430
1431 static struct tevent_req *winexe_ctrl_send(
1432         TALLOC_CTX *mem_ctx,
1433         struct tevent_context *ev,
1434         struct cli_state *cli,
1435         const char *cmd)
1436 {
1437         struct tevent_req *req, *subreq;
1438         struct winexe_ctrl_state *state;
1439
1440         req = tevent_req_create(mem_ctx, &state,
1441                                 struct winexe_ctrl_state);
1442         if (req == NULL) {
1443                 return NULL;
1444         }
1445         state->ev = ev;
1446         state->cli = cli;
1447
1448         state->cmd = talloc_asprintf(state, "run %s\n", cmd);
1449         if (tevent_req_nomem(state->cmd, req)) {
1450                 return tevent_req_post(req, ev);
1451         }
1452
1453         subreq = cli_ntcreate_send(
1454                 state,
1455                 state->ev,
1456                 state->cli,
1457                 "\\" PIPE_NAME,
1458                 0,
1459                 SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
1460                 SEC_RIGHTS_FILE_EXECUTE,
1461                 0,              /* FileAttributes */
1462                 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1463                 FILE_OPEN,      /* CreateDisposition */
1464                 0,              /* CreateOptions */
1465                 0);             /* SecurityFlags */
1466         if (tevent_req_nomem(subreq, req)) {
1467                 return tevent_req_post(req, ev);
1468         }
1469         tevent_req_set_callback(subreq, winexe_ctrl_opened, req);
1470         return req;
1471 }
1472
1473 static void winexe_ctrl_opened(struct tevent_req *subreq)
1474 {
1475         struct tevent_req *req = tevent_req_callback_data(
1476                 subreq, struct tevent_req);
1477         struct winexe_ctrl_state *state = tevent_req_data(
1478                 req, struct winexe_ctrl_state);
1479         int timeout;
1480         NTSTATUS status;
1481         static const char cmd[] = "get codepage\nget version\n";
1482
1483         status = cli_ntcreate_recv(subreq, &state->ctrl_pipe, NULL);
1484         TALLOC_FREE(subreq);
1485         if (tevent_req_nterror(req, status)) {
1486                 return;
1487         }
1488
1489         timeout = state->cli->timeout;
1490         state->cli->timeout = 0;
1491
1492         subreq = cli_read_send(
1493                 state,
1494                 state->ev,
1495                 state->cli,
1496                 state->ctrl_pipe,
1497                 state->ctrl_inbuf,
1498                 0,
1499                 sizeof(state->ctrl_inbuf)-1);
1500
1501         state->cli->timeout = timeout;
1502
1503         if (tevent_req_nomem(subreq, req)) {
1504                 return;
1505         }
1506         tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
1507
1508         subreq = cli_writeall_send(
1509                 state,
1510                 state->ev,
1511                 state->cli,
1512                 state->ctrl_pipe,
1513                 0,
1514                 (const uint8_t *)cmd,
1515                 0,
1516                 strlen(cmd));
1517         if (tevent_req_nomem(subreq, req)) {
1518                 return;
1519         }
1520         tevent_req_set_callback(subreq, winexe_ctrl_wrote_version, req);
1521 }
1522
1523 static void winexe_ctrl_got_read(struct tevent_req *subreq)
1524 {
1525         struct tevent_req *req = tevent_req_callback_data(
1526                 subreq, struct tevent_req);
1527         struct winexe_ctrl_state *state = tevent_req_data(
1528                 req, struct winexe_ctrl_state);
1529         NTSTATUS status;
1530         int timeout;
1531         size_t received;
1532         unsigned int version, return_code;
1533         int ret;
1534
1535         status = cli_read_recv(subreq, &received);
1536         TALLOC_FREE(subreq);
1537
1538         if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
1539                 subreq = cli_close_send(
1540                         state,
1541                         state->ev,
1542                         state->cli,
1543                         state->ctrl_pipe);
1544                 if (tevent_req_nomem(subreq, req)) {
1545                         return;
1546                 }
1547                 tevent_req_set_callback(subreq, winexe_ctrl_pipe_closed, req);
1548                 return;
1549         }
1550         if (tevent_req_nterror(req, status)) {
1551                 return;
1552         }
1553
1554         DBG_DEBUG("Got %zu bytes\n", received);
1555
1556         timeout = state->cli->timeout;
1557         state->cli->timeout = 0;
1558
1559         subreq = cli_read_send(
1560                 state,
1561                 state->ev,
1562                 state->cli,
1563                 state->ctrl_pipe,
1564                 state->ctrl_inbuf,
1565                 0,
1566                 sizeof(state->ctrl_inbuf)-1);
1567
1568         state->cli->timeout = timeout;
1569
1570         if (tevent_req_nomem(subreq, req)) {
1571                 return;
1572         }
1573         tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
1574
1575         ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &version);
1576         if (ret == 1) {
1577                 DBG_DEBUG("Got version %x\n", version);
1578
1579                 subreq = cli_writeall_send(
1580                         state,
1581                         state->ev,
1582                         state->cli,
1583                         state->ctrl_pipe,
1584                         0,
1585                         (const uint8_t *)state->cmd,
1586                         0,
1587                         strlen(state->cmd));
1588                 if (tevent_req_nomem(subreq, req)) {
1589                         return;
1590                 }
1591                 tevent_req_set_callback(subreq, winexe_ctrl_wrote_cmd, req);
1592                 return;
1593         }
1594
1595         ret = strncmp(state->ctrl_inbuf, "std_io_err ", strlen("std_io_err "));
1596         if (ret == 0) {
1597                 char *p = state->ctrl_inbuf + 11;
1598                 char *q = strchr(state->ctrl_inbuf, '\n');
1599                 char *postfix;
1600                 size_t postfix_len;
1601
1602                 if (q == NULL) {
1603                         DBG_DEBUG("Got invalid pipe postfix\n");
1604                         return;
1605                 }
1606
1607                 postfix_len = q - p;
1608
1609                 postfix = talloc_strndup(state, p, postfix_len);
1610                 if (tevent_req_nomem(postfix, req)) {
1611                         return;
1612                 }
1613
1614                 DBG_DEBUG("Got pipe postfix %s\n", postfix);
1615
1616                 subreq = winexe_pipes_send(
1617                         state,
1618                         state->ev,
1619                         state->cli,
1620                         postfix);
1621                 if (tevent_req_nomem(subreq, req)) {
1622                         return;
1623                 }
1624                 tevent_req_set_callback(subreq, winexe_ctrl_pipes_done, req);
1625
1626                 state->pipes_req = subreq;
1627
1628                 return;
1629         }
1630
1631         ret = strncmp(state->ctrl_inbuf, "error ", strlen("error "));
1632         if (ret == 0) {
1633                 printf("Error: %s", state->ctrl_inbuf);
1634                 return;
1635         }
1636
1637         ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &return_code);
1638         if (ret == 1) {
1639                 state->return_code = return_code;
1640                 return;
1641         }
1642 }
1643
1644 static void winexe_ctrl_wrote_version(struct tevent_req *subreq)
1645 {
1646         struct tevent_req *req = tevent_req_callback_data(
1647                 subreq, struct tevent_req);
1648         NTSTATUS status;
1649
1650         status = cli_writeall_recv(subreq, NULL);
1651         TALLOC_FREE(subreq);
1652         if (tevent_req_nterror(req, status)) {
1653                 return;
1654         }
1655 }
1656
1657 static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq)
1658 {
1659         struct tevent_req *req = tevent_req_callback_data(
1660                 subreq, struct tevent_req);
1661         NTSTATUS status;
1662
1663         status = cli_writeall_recv(subreq, NULL);
1664         TALLOC_FREE(subreq);
1665         if (tevent_req_nterror(req, status)) {
1666                 return;
1667         }
1668 }
1669
1670 static void winexe_ctrl_pipe_closed(struct tevent_req *subreq)
1671 {
1672         struct tevent_req *req = tevent_req_callback_data(
1673                 subreq, struct tevent_req);
1674         struct winexe_ctrl_state *state = tevent_req_data(
1675                 req, struct winexe_ctrl_state);
1676         NTSTATUS status;
1677
1678         status = cli_close_recv(subreq);
1679         TALLOC_FREE(subreq);
1680         if (tevent_req_nterror(req, status)) {
1681                 return;
1682         }
1683
1684         state->ctrl_pipe_done = true;
1685         if (state->pipes_req == NULL) {
1686                 tevent_req_done(req);
1687         }
1688 }
1689
1690 static void winexe_ctrl_pipes_done(struct tevent_req *subreq)
1691 {
1692         struct tevent_req *req = tevent_req_callback_data(
1693                 subreq, struct tevent_req);
1694         struct winexe_ctrl_state *state = tevent_req_data(
1695                 req, struct winexe_ctrl_state);
1696         NTSTATUS status;
1697
1698         status = winexe_pipes_recv(subreq);
1699         TALLOC_FREE(subreq);
1700         if (tevent_req_nterror(req, status)) {
1701                 return;
1702         }
1703
1704         state->pipes_req = NULL;
1705         if (state->ctrl_pipe_done) {
1706                 tevent_req_done(req);
1707         }
1708 }
1709
1710 static NTSTATUS winexe_ctrl_recv(struct tevent_req *req,
1711                                  int *preturn_code)
1712 {
1713         struct winexe_ctrl_state *state = tevent_req_data(
1714                 req, struct winexe_ctrl_state);
1715         NTSTATUS status;
1716
1717         if (tevent_req_is_nterror(req, &status)) {
1718                 return status;
1719         }
1720         if (preturn_code != NULL) {
1721                 *preturn_code = state->return_code;
1722         }
1723         return NT_STATUS_OK;
1724 }
1725
1726 static NTSTATUS winexe_ctrl(struct cli_state *cli,
1727                             const char *cmd,
1728                             int *preturn_code)
1729 {
1730         struct tevent_context *ev = NULL;
1731         struct tevent_req *req = NULL;
1732         NTSTATUS status = NT_STATUS_NO_MEMORY;
1733         bool ok;
1734
1735         ev = samba_tevent_context_init(cli);
1736         if (ev == NULL) {
1737                 goto done;
1738         }
1739         req = winexe_ctrl_send(ev, ev, cli, cmd);
1740         if (req == NULL) {
1741                 goto done;
1742         }
1743         ok = tevent_req_poll_ntstatus(req, ev, &status);
1744         if (!ok) {
1745                 goto done;
1746         }
1747         status = winexe_ctrl_recv(req, preturn_code);
1748 done:
1749         TALLOC_FREE(req);
1750         TALLOC_FREE(ev);
1751         return status;
1752 }
1753
1754 #ifdef HAVE_WINEXE_CC_WIN32
1755 const DATA_BLOB *winexesvc32_exe_binary(void);
1756 #endif
1757
1758 #ifdef HAVE_WINEXE_CC_WIN64
1759 const DATA_BLOB *winexesvc64_exe_binary(void);
1760 #endif
1761
1762 int main(int argc, const char *argv[])
1763 {
1764         TALLOC_CTX *frame = talloc_stackframe();
1765         struct program_options options = {0};
1766         struct loadparm_context *lp_ctx;
1767         struct cli_state *cli;
1768         const char *service_name = SERVICE_NAME;
1769         char *service_filename = NULL;
1770 #ifdef HAVE_WINEXE_CC_WIN32
1771         const DATA_BLOB *winexesvc32_exe = winexesvc32_exe_binary();
1772 #else
1773         const DATA_BLOB *winexesvc32_exe = NULL;
1774 #endif
1775 #ifdef HAVE_WINEXE_CC_WIN64
1776         const DATA_BLOB *winexesvc64_exe = winexesvc64_exe_binary();
1777 #else
1778         const DATA_BLOB *winexesvc64_exe = NULL;
1779 #endif
1780         NTSTATUS status;
1781         int ret = 1;
1782         int return_code = 0;
1783
1784         lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
1785         if (lp_ctx == NULL) {
1786                 fprintf(stderr, "loadparm_init_s3 failed\n");
1787                 goto done;
1788         }
1789
1790         smb_init_locale();
1791         setup_logging("winexe", DEBUG_STDOUT);
1792
1793         lp_load_global(get_dyn_CONFIGFILE());
1794
1795         parse_args(argc, argv, frame, &options, lp_ctx);
1796
1797         if (options.cmd == NULL) {
1798                 fprintf(stderr, "no cmd given\n");
1799                 goto done;
1800         }
1801
1802         service_filename = talloc_asprintf(frame, "%s.exe", service_name);
1803         if (service_filename == NULL) {
1804                 DBG_WARNING("talloc_asprintf failed\n");
1805                 goto done;
1806         }
1807
1808         status = cli_full_connection_creds(
1809                 &cli,
1810                 NULL,
1811                 options.hostname,
1812                 NULL,
1813                 445,
1814                 "IPC$",
1815                 "?????",
1816                 options.credentials,
1817                 0,
1818                 0);
1819
1820         if (!NT_STATUS_IS_OK(status)) {
1821                 DBG_WARNING("cli_full_connection_creds failed: %s\n",
1822                             nt_errstr(status));
1823                 goto done;
1824         }
1825
1826         status = winexe_svc_install(
1827                 cli,
1828                 options.hostname,
1829                 service_name,
1830                 service_filename,
1831                 winexesvc32_exe,
1832                 winexesvc64_exe,
1833                 options.credentials,
1834                 options.flags);
1835         if (!NT_STATUS_IS_OK(status)) {
1836                 DBG_WARNING("winexe_svc_install failed: %s\n",
1837                             nt_errstr(status));
1838                 goto done;
1839         }
1840
1841         status = winexe_ctrl(cli, options.cmd, &return_code);
1842         if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
1843                 /* Normal finish */
1844                 status = NT_STATUS_OK;
1845         }
1846         if (!NT_STATUS_IS_OK(status)) {
1847                 DBG_WARNING("cli_ctrl failed: %s\n",
1848                             nt_errstr(status));
1849                 goto done;
1850         }
1851
1852         if (options.flags & SVC_UNINSTALL) {
1853                 status = winexe_svc_uninstall(
1854                         cli,
1855                         service_name);
1856                 if (!NT_STATUS_IS_OK(status)) {
1857                         DBG_WARNING("winexe_svc_uninstall failed: %s\n",
1858                                     nt_errstr(status));
1859                         goto done;
1860                 }
1861         }
1862
1863         ret = return_code;
1864 done:
1865         TALLOC_FREE(frame);
1866         return ret;
1867 }