summaryrefslogtreecommitdiffstats
path: root/lib/cmdline
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cmdline')
-rw-r--r--lib/cmdline/closefrom_except.c93
-rw-r--r--lib/cmdline/closefrom_except.h29
-rw-r--r--lib/cmdline/cmdline.c1415
-rw-r--r--lib/cmdline/cmdline.h319
-rw-r--r--lib/cmdline/cmdline_private.h121
-rw-r--r--lib/cmdline/cmdline_s3.c152
-rw-r--r--lib/cmdline/cmdline_s4.c125
-rw-r--r--lib/cmdline/tests/test_cmdline.c97
-rw-r--r--lib/cmdline/wscript35
9 files changed, 2386 insertions, 0 deletions
diff --git a/lib/cmdline/closefrom_except.c b/lib/cmdline/closefrom_except.c
new file mode 100644
index 0000000..fe4e0cc
--- /dev/null
+++ b/lib/cmdline/closefrom_except.c
@@ -0,0 +1,93 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "replace.h"
+#include "closefrom_except.h"
+#include <popt.h>
+
+int closefrom_except(int lower, int *fds, size_t num_fds)
+{
+ size_t i;
+ int max_keep = -1;
+ int fd, ret;
+
+ for (i=0; i<num_fds; i++) {
+ max_keep = MAX(max_keep, fds[i]);
+ }
+ if (max_keep == -1) {
+ return 0;
+ }
+
+ for (fd = lower; fd < max_keep; fd++) {
+ bool keep = false;
+
+ /*
+ * O(num_fds*max_keep), but we expect the number of
+ * fds to keep to be very small, typically 0,1,2 and
+ * very few more.
+ */
+ for (i=0; i<num_fds; i++) {
+ if (fd == fds[i]) {
+ keep = true;
+ break;
+ }
+ }
+ if (keep) {
+ continue;
+ }
+ ret = close(fd);
+ if ((ret == -1) && (errno != EBADF)) {
+ return errno;
+ }
+ }
+
+ closefrom(MAX(lower, max_keep+1));
+ return 0;
+}
+
+int closefrom_except_fd_params(
+ int lower,
+ size_t num_fd_params,
+ const char *fd_params[],
+ int argc,
+ const char *argv[])
+{
+ int fds[num_fd_params];
+ size_t i;
+ struct poptOption long_options[num_fd_params + 1];
+ poptContext pc;
+ int ret;
+
+ for (i=0; i<num_fd_params; i++) {
+ fds[i] = -1;
+ long_options[i] = (struct poptOption) {
+ .longName = fd_params[i],
+ .argInfo = POPT_ARG_INT,
+ .arg = &fds[i],
+ };
+ }
+ long_options[num_fd_params] = (struct poptOption) { .longName=NULL, };
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+
+ while ((ret = poptGetNextOpt(pc)) != -1) {
+ /* do nothing */
+ }
+
+ poptFreeContext(pc);
+
+ ret = closefrom_except(lower, fds, ARRAY_SIZE(fds));
+ return ret;
+}
diff --git a/lib/cmdline/closefrom_except.h b/lib/cmdline/closefrom_except.h
new file mode 100644
index 0000000..d696ebf
--- /dev/null
+++ b/lib/cmdline/closefrom_except.h
@@ -0,0 +1,29 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIB_CLOSEFROM_EXCEPT_H__
+#define __LIB_CLOSEFROM_EXCEPT_H__
+
+#include "replace.h"
+
+int closefrom_except(int lower, int *fds, size_t num_fds);
+int closefrom_except_fd_params(
+ int lower,
+ size_t num_fd_params,
+ const char *fd_params[],
+ int argc,
+ const char *argv[]);
+
+#endif
diff --git a/lib/cmdline/cmdline.c b/lib/cmdline/cmdline.c
new file mode 100644
index 0000000..db96214
--- /dev/null
+++ b/lib/cmdline/cmdline.c
@@ -0,0 +1,1415 @@
+/*
+ * Copyright (c) 2020 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "lib/param/param.h"
+#include "dynconfig/dynconfig.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/smb/smb_util.h"
+#include "cmdline_private.h"
+#include "lib/util/util_process.h"
+
+#include <samba/version.h>
+
+static TALLOC_CTX *cmdline_mem_ctx;
+static struct loadparm_context *cmdline_lp_ctx;
+static struct cli_credentials *cmdline_creds;
+static samba_cmdline_load_config cmdline_load_config_fn;
+static struct samba_cmdline_daemon_cfg cmdline_daemon_cfg;
+
+static NTSTATUS (*cli_credentials_set_machine_account_fn)(
+ struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx) =
+ cli_credentials_set_machine_account;
+
+/* PRIVATE */
+bool samba_cmdline_set_talloc_ctx(TALLOC_CTX *mem_ctx)
+{
+ if (cmdline_mem_ctx != NULL) {
+ return false;
+ }
+
+ cmdline_mem_ctx = mem_ctx;
+ return true;
+}
+
+TALLOC_CTX *samba_cmdline_get_talloc_ctx(void)
+{
+ return cmdline_mem_ctx;
+}
+
+static void _samba_cmdline_talloc_log(const char *message)
+{
+ D_ERR("%s", message);
+}
+
+bool samba_cmdline_init_common(TALLOC_CTX *mem_ctx)
+{
+ bool ok;
+
+ ok = samba_cmdline_set_talloc_ctx(mem_ctx);
+ if (!ok) {
+ return false;
+ }
+
+ cmdline_daemon_cfg = (struct samba_cmdline_daemon_cfg) {
+ .fork = true,
+ };
+
+ fault_setup();
+
+ /*
+ * Log to stderr by default.
+ * This can be changed to stdout using the option: --debug-stdout
+ */
+ setup_logging(getprogname(), DEBUG_DEFAULT_STDERR);
+
+ talloc_set_log_fn(_samba_cmdline_talloc_log);
+ talloc_set_abort_fn(smb_panic);
+
+ return true;
+}
+
+bool samba_cmdline_set_load_config_fn(samba_cmdline_load_config fn)
+{
+ cmdline_load_config_fn = fn;
+ return true;
+}
+
+/* PUBLIC */
+bool samba_cmdline_set_lp_ctx(struct loadparm_context *lp_ctx)
+{
+ if (lp_ctx == NULL) {
+ return false;
+ }
+ cmdline_lp_ctx = lp_ctx;
+
+ return true;
+}
+
+struct loadparm_context *samba_cmdline_get_lp_ctx(void)
+{
+ return cmdline_lp_ctx;
+}
+
+bool samba_cmdline_set_creds(struct cli_credentials *creds)
+{
+ if (creds == NULL) {
+ return false;
+ }
+
+ TALLOC_FREE(cmdline_creds);
+ cmdline_creds = creds;
+
+ return true;
+}
+
+struct cli_credentials *samba_cmdline_get_creds(void)
+{
+ return cmdline_creds;
+}
+
+struct samba_cmdline_daemon_cfg *samba_cmdline_get_daemon_cfg(void)
+{
+ return &cmdline_daemon_cfg;
+}
+
+void samba_cmdline_set_machine_account_fn(
+ NTSTATUS (*fn) (struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx))
+{
+ cli_credentials_set_machine_account_fn = fn;
+}
+
+bool samba_cmdline_burn(int argc, char *argv[])
+{
+ bool burnt = false;
+ bool found = false;
+ bool is_user = false;
+ char *p = NULL;
+ int i;
+ size_t ulen = 0;
+
+ for (i = 0; i < argc; i++) {
+ p = argv[i];
+ if (p == NULL) {
+ return false;
+ }
+
+ /*
+ * Take care that this list must be in longest-match
+ * first order
+ */
+ if (strncmp(p, "-U", 2) == 0) {
+ ulen = 2;
+ found = true;
+ is_user = true;
+ } else if (strncmp(p, "--user", 6) == 0) {
+ ulen = 6;
+ found = true;
+ is_user = true;
+ } else if (strncmp(p, "--password2", 11) == 0) {
+ ulen = 11;
+ found = true;
+ } else if (strncmp(p, "--password", 10) == 0) {
+ ulen = 10;
+ found = true;
+ } else if (strncmp(p, "--newpassword", 13) == 0) {
+ ulen = 13;
+ found = true;
+ }
+
+ if (found) {
+ char *q = NULL;
+
+ if (strlen(p) == ulen) {
+ continue;
+ }
+
+ if (is_user) {
+ q = strchr_m(p, '%');
+ if (q != NULL) {
+ p = q;
+ }
+ } else {
+ p += ulen;
+ }
+
+ memset_s(p, strlen(p), '\0', strlen(p));
+ found = false;
+ is_user = false;
+ burnt = true;
+ }
+ }
+ return burnt;
+}
+
+static bool is_popt_table_end(const struct poptOption *o)
+{
+ if (o->longName == NULL &&
+ o->shortName == 0 &&
+ o->argInfo == 0 &&
+ o->arg == NULL &&
+ o->val == 0 &&
+ o->descrip == NULL &&
+ o->argDescrip == NULL) {
+ return true;
+ }
+
+ return false;
+}
+
+static void find_duplicates(const struct poptOption *needle,
+ const struct poptOption *haystack,
+ size_t *count)
+{
+ for(;
+ !is_popt_table_end(haystack);
+ haystack++) {
+ switch (haystack->argInfo) {
+ case POPT_ARG_INCLUDE_TABLE:
+ if (haystack->arg != NULL) {
+ find_duplicates(needle, haystack->arg, count);
+ }
+
+ break;
+ default:
+ if (needle->shortName != 0 &&
+ needle->shortName == haystack->shortName) {
+ (*count)++;
+ break;
+ }
+
+ if (needle->longName != NULL &&
+ haystack->longName != NULL &&
+ strequal(needle->longName, haystack->longName)) {
+ (*count)++;
+ break;
+ }
+ break;
+ }
+
+ if (*count > 1) {
+ return;
+ }
+ }
+}
+
+static bool cmdline_sanity_checker(const struct poptOption *current_opts,
+ const struct poptOption *full_opts)
+{
+ const struct poptOption *o = current_opts;
+
+ for(;
+ !is_popt_table_end(o);
+ o++) {
+ bool ok;
+
+ switch (o->argInfo) {
+ case POPT_ARG_INCLUDE_TABLE:
+ if (o->arg != NULL) {
+ ok = cmdline_sanity_checker(o->arg, full_opts);
+ if (!ok) {
+ return false;
+ }
+ }
+
+ break;
+ default:
+ if (o->longName != NULL || o->shortName != 0) {
+ size_t count = 0;
+
+ find_duplicates(o, full_opts, &count);
+ if (count > 1) {
+ DBG_ERR("Duplicate option '--%s|-%c' "
+ "detected!\n",
+ o->longName,
+ o->shortName != 0 ?
+ o->shortName :
+ '-');
+ return false;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool samba_cmdline_sanity_check(const struct poptOption *opts)
+{
+ return cmdline_sanity_checker(opts, opts);
+}
+
+poptContext samba_popt_get_context(const char * name,
+ int argc, const char ** argv,
+ const struct poptOption * options,
+ unsigned int flags)
+{
+#ifdef DEVELOPER
+ bool ok;
+
+ ok = samba_cmdline_sanity_check(options);
+ if (!ok) {
+ return NULL;
+ }
+#endif
+ process_save_binary_name(name);
+ return poptGetContext(name, argc, argv, options, flags);
+}
+
+/**********************************************************
+ * COMMON SAMBA POPT
+ **********************************************************/
+
+static bool log_to_file;
+
+static bool set_logfile(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *log_basename,
+ const char *process_name,
+ bool from_cmdline)
+{
+ bool ok = false;
+ char *new_logfile = talloc_asprintf(mem_ctx,
+ "%s/log.%s",
+ log_basename,
+ process_name);
+ if (new_logfile == NULL) {
+ return false;
+ }
+
+ if (from_cmdline) {
+ ok = lpcfg_set_cmdline(lp_ctx,
+ "log file",
+ new_logfile);
+ } else {
+ ok = lpcfg_do_global_parameter(lp_ctx,
+ "log file",
+ new_logfile);
+ }
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set log to %s\n",
+ new_logfile);
+ TALLOC_FREE(new_logfile);
+ return false;
+ }
+ debug_set_logfile(new_logfile);
+ TALLOC_FREE(new_logfile);
+
+ return true;
+}
+
+static void popt_samba_callback(poptContext popt_ctx,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg, const void *data)
+{
+ TALLOC_CTX *mem_ctx = samba_cmdline_get_talloc_ctx();
+ struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
+ const char *pname = NULL;
+ bool ok;
+
+ /* Find out basename of current program */
+ pname = getprogname();
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ if (lp_ctx == NULL) {
+ fprintf(stderr,
+ "Command line parsing not initialized!\n");
+ exit(1);
+ }
+ ok = set_logfile(mem_ctx,
+ lp_ctx,
+ get_dyn_LOGFILEBASE(),
+ pname,
+ false);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set log file for %s\n",
+ pname);
+ exit(1);
+ }
+ return;
+ }
+
+ if (reason == POPT_CALLBACK_REASON_POST) {
+ ok = cmdline_load_config_fn();
+ if (!ok) {
+ fprintf(stderr,
+ "%s - Failed to load config file!\n",
+ getprogname());
+ exit(1);
+ }
+
+ if (log_to_file) {
+ const struct loadparm_substitution *lp_sub =
+ lpcfg_noop_substitution();
+ char *logfile = NULL;
+
+ logfile = lpcfg_logfile(lp_ctx, lp_sub, mem_ctx);
+ if (logfile == NULL) {
+ fprintf(stderr,
+ "Failed to setup logging to file!");
+ exit(1);
+ }
+ debug_set_logfile(logfile);
+ setup_logging(logfile, DEBUG_FILE);
+ TALLOC_FREE(logfile);
+ }
+
+ return;
+ }
+
+ switch(opt->val) {
+ case OPT_LEAK_REPORT:
+ talloc_enable_leak_report();
+ break;
+ case OPT_LEAK_REPORT_FULL:
+ talloc_enable_leak_report_full();
+ break;
+ case OPT_OPTION:
+ if (arg != NULL) {
+ ok = lpcfg_set_option(lp_ctx, arg);
+ if (!ok) {
+ fprintf(stderr, "Error setting option '%s'\n", arg);
+ exit(1);
+ }
+ }
+ break;
+ case 'd':
+ if (arg != NULL) {
+ ok = lpcfg_set_cmdline(lp_ctx, "log level", arg);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set debug level to: %s\n",
+ arg);
+ exit(1);
+ }
+ }
+ break;
+ case OPT_DEBUG_STDOUT:
+ setup_logging(pname, DEBUG_STDOUT);
+ break;
+ case OPT_CONFIGFILE:
+ if (arg != NULL) {
+ set_dyn_CONFIGFILE(arg);
+ }
+ break;
+ case 'l':
+ if (arg != NULL) {
+ ok = set_logfile(mem_ctx, lp_ctx, arg, pname, true);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set log file for %s\n",
+ arg);
+ exit(1);
+ }
+ log_to_file = true;
+
+ set_dyn_LOGFILEBASE(arg);
+ }
+ break;
+ }
+}
+
+static struct poptOption popt_common_debug[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
+ .arg = (void *)popt_samba_callback,
+ },
+ {
+ .longName = "debuglevel",
+ .shortName = 'd',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'd',
+ .descrip = "Set debug level",
+ .argDescrip = "DEBUGLEVEL",
+ },
+ {
+ .longName = "debug-stdout",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_DEBUG_STDOUT,
+ .descrip = "Send debug output to standard output",
+ },
+ POPT_TABLEEND
+};
+
+static struct poptOption popt_common_option[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
+ .arg = (void *)popt_samba_callback,
+ },
+ {
+ .longName = "option",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_OPTION,
+ .descrip = "Set smb.conf option from command line",
+ .argDescrip = "name=value",
+ },
+ POPT_TABLEEND
+};
+
+static struct poptOption popt_common_config[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
+ .arg = (void *)popt_samba_callback,
+ },
+ {
+ .longName = "configfile",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_CONFIGFILE,
+ .descrip = "Use alternative configuration file",
+ .argDescrip = "CONFIGFILE",
+ },
+ POPT_TABLEEND
+};
+
+static struct poptOption popt_common_samba[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
+ .arg = (void *)popt_samba_callback,
+ },
+ {
+ .longName = "debuglevel",
+ .shortName = 'd',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'd',
+ .descrip = "Set debug level",
+ .argDescrip = "DEBUGLEVEL",
+ },
+ {
+ .longName = "debug-stdout",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_DEBUG_STDOUT,
+ .descrip = "Send debug output to standard output",
+ },
+ {
+ .longName = "configfile",
+ .shortName = 's',
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_CONFIGFILE,
+ .descrip = "Use alternative configuration file",
+ .argDescrip = "CONFIGFILE",
+ },
+ {
+ .longName = "option",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_OPTION,
+ .descrip = "Set smb.conf option from command line",
+ .argDescrip = "name=value",
+ },
+ {
+ .longName = "log-basename",
+ .shortName = 'l',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'l',
+ .descrip = "Basename for log/debug files",
+ .argDescrip = "LOGFILEBASE",
+ },
+ {
+ .longName = "leak-report",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LEAK_REPORT,
+ .descrip = "enable talloc leak reporting on exit",
+ },
+ {
+ .longName = "leak-report-full",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LEAK_REPORT_FULL,
+ .descrip = "enable full talloc leak reporting on exit",
+ },
+ POPT_TABLEEND
+};
+
+static struct poptOption popt_common_samba_ldb[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
+ .arg = (void *)popt_samba_callback,
+ },
+ {
+ .longName = "debuglevel",
+ .shortName = 'd',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'd',
+ .descrip = "Set debug level",
+ .argDescrip = "DEBUGLEVEL",
+ },
+ {
+ .longName = "debug-stdout",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_DEBUG_STDOUT,
+ .descrip = "Send debug output to standard output",
+ },
+ {
+ .longName = "configfile",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_CONFIGFILE,
+ .descrip = "Use alternative configuration file",
+ .argDescrip = "CONFIGFILE",
+ },
+ {
+ .longName = "option",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_OPTION,
+ .descrip = "Set smb.conf option from command line",
+ .argDescrip = "name=value",
+ },
+ {
+ .longName = "log-basename",
+ .shortName = 'l',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'l',
+ .descrip = "Basename for log/debug files",
+ .argDescrip = "LOGFILEBASE",
+ },
+ {
+ .longName = "leak-report",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LEAK_REPORT,
+ .descrip = "enable talloc leak reporting on exit",
+ },
+ {
+ .longName = "leak-report-full",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_LEAK_REPORT_FULL,
+ .descrip = "enable full talloc leak reporting on exit",
+ },
+ POPT_TABLEEND
+};
+
+/**********************************************************
+ * CONNECTION POPT
+ **********************************************************/
+
+static void popt_connection_callback(poptContext popt_ctx,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg,
+ const void *data)
+{
+ struct loadparm_context *lp_ctx = cmdline_lp_ctx;
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ if (lp_ctx == NULL) {
+ fprintf(stderr,
+ "Command line parsing not initialized!\n");
+ exit(1);
+ }
+ return;
+ }
+
+ switch(opt->val) {
+ case 'O':
+ if (arg != NULL) {
+ lpcfg_set_cmdline(lp_ctx, "socket options", arg);
+ }
+ break;
+ case 'R':
+ if (arg != NULL) {
+ lpcfg_set_cmdline(lp_ctx, "name resolve order", arg);
+ }
+ break;
+ case 'm':
+ if (arg != NULL) {
+ lpcfg_set_cmdline(lp_ctx, "client max protocol", arg);
+ }
+ break;
+ case OPT_NETBIOS_SCOPE:
+ if (arg != NULL) {
+ lpcfg_set_cmdline(lp_ctx, "netbios scope", arg);
+ }
+ break;
+ case 'n':
+ if (arg != NULL) {
+ lpcfg_set_cmdline(lp_ctx, "netbios name", arg);
+ }
+ break;
+ case 'W':
+ if (arg != NULL) {
+ lpcfg_set_cmdline(lp_ctx, "workgroup", arg);
+ }
+ break;
+ case 'r':
+ if (arg != NULL) {
+ lpcfg_set_cmdline(lp_ctx, "realm", arg);
+ }
+ break;
+ }
+}
+
+static struct poptOption popt_common_connection[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE,
+ .arg = (void *)popt_connection_callback,
+ },
+ {
+ .longName = "name-resolve",
+ .shortName = 'R',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'R',
+ .descrip = "Use these name resolution services only",
+ .argDescrip = "NAME-RESOLVE-ORDER",
+ },
+ {
+ .longName = "socket-options",
+ .shortName = 'O',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'O',
+ .descrip = "socket options to use",
+ .argDescrip = "SOCKETOPTIONS",
+ },
+ {
+ .longName = "max-protocol",
+ .shortName = 'm',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'm',
+ .descrip = "Set max protocol level",
+ .argDescrip = "MAXPROTOCOL",
+ },
+ {
+ .longName = "netbiosname",
+ .shortName = 'n',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'n',
+ .descrip = "Primary netbios name",
+ .argDescrip = "NETBIOSNAME",
+ },
+ {
+ .longName = "netbios-scope",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_NETBIOS_SCOPE,
+ .descrip = "Use this Netbios scope",
+ .argDescrip = "SCOPE",
+ },
+ {
+ .longName = "workgroup",
+ .shortName = 'W',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'W',
+ .descrip = "Set the workgroup name",
+ .argDescrip = "WORKGROUP",
+ },
+ {
+ .longName = "realm",
+ .argInfo = POPT_ARG_STRING,
+ .val = 'r',
+ .descrip = "Set the realm name",
+ .argDescrip = "REALM",
+ },
+ POPT_TABLEEND
+};
+
+/**********************************************************
+ * CREDENTIALS POPT
+ **********************************************************/
+
+static bool skip_password_callback;
+static bool machine_account_pending;
+
+static void popt_common_credentials_callback(poptContext popt_ctx,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg,
+ const void *data)
+{
+ struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ bool ok;
+
+ if (reason == POPT_CALLBACK_REASON_PRE) {
+ if (creds == NULL) {
+ fprintf(stderr,
+ "Command line parsing not initialized!\n");
+ exit(1);
+ }
+ return;
+ }
+
+ if (reason == POPT_CALLBACK_REASON_POST) {
+ const char *username = NULL;
+ enum credentials_obtained username_obtained =
+ CRED_UNINITIALISED;
+ enum credentials_obtained password_obtained =
+ CRED_UNINITIALISED;
+
+ /*
+ * This calls cli_credentials_set_conf() to get the defaults
+ * form smb.conf and set the winbind separator.
+ *
+ * Just warn that we can't read the smb.conf. There might not be
+ * one available or we want to ignore it.
+ */
+ ok = cli_credentials_guess(creds, lp_ctx);
+ if (!ok) {
+ fprintf(stderr,
+ "Unable to read defaults from smb.conf\n");
+ }
+
+ (void)cli_credentials_get_password_and_obtained(creds,
+ &password_obtained);
+ if (!skip_password_callback &&
+ password_obtained < CRED_CALLBACK) {
+ ok = cli_credentials_set_cmdline_callbacks(creds);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set cmdline password "
+ "callback\n");
+ exit(1);
+ }
+ }
+
+ if (machine_account_pending) {
+ NTSTATUS status;
+
+ status = cli_credentials_set_machine_account_fn(
+ creds, lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "Failed to set machine account: %s\n",
+ nt_errstr(status));
+ exit(1);
+ }
+ }
+
+ /*
+ * When we set the username during the handling of the options
+ * passed to the binary we haven't loaded the config yet. This
+ * means that we didn't take the 'winbind separator' into
+ * account.
+ *
+ * The username might contain the domain name and thus it
+ * hasn't been correctly parsed yet. If we have a username we
+ * need to set it again to run the string parser for the
+ * username correctly.
+ */
+ username =
+ cli_credentials_get_username_and_obtained(
+ creds, &username_obtained);
+ if (username_obtained == CRED_SPECIFIED &&
+ username != NULL && username[0] != '\0') {
+ cli_credentials_parse_string(creds,
+ username,
+ CRED_SPECIFIED);
+ }
+
+ return;
+ }
+
+ switch(opt->val) {
+ case 'U':
+ if (arg != NULL) {
+ cli_credentials_parse_string(creds,
+ arg,
+ CRED_SPECIFIED);
+ }
+ break;
+ case OPT_PASSWORD:
+ if (arg != NULL) {
+ ok = cli_credentials_set_password(creds,
+ arg,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set password!\n");
+ exit(1);
+ }
+
+ skip_password_callback = true;
+ }
+ break;
+ case OPT_NT_HASH:
+ cli_credentials_set_password_will_be_nt_hash(creds, true);
+ break;
+ case 'A':
+ if (arg != NULL) {
+ ok = cli_credentials_parse_file(creds,
+ arg,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set parse authentication file!\n");
+ exit(1);
+ }
+ skip_password_callback = true;
+ }
+ break;
+ case 'N':
+ ok = cli_credentials_set_password(creds,
+ NULL,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set password!\n");
+ exit(1);
+ }
+ skip_password_callback = true;
+ break;
+ case 'P':
+ /*
+ * Later, after this is all over, get the machine account
+ * details from the secrets.(l|t)db.
+ */
+ machine_account_pending = true;
+ break;
+ case OPT_SIMPLE_BIND_DN:
+ if (arg != NULL) {
+ ok = cli_credentials_set_bind_dn(creds, arg);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set bind DN!\n");
+ exit(1);
+ }
+ }
+ break;
+ case OPT_USE_KERBEROS: {
+ int32_t use_kerberos = INT_MIN;
+ if (arg == NULL) {
+ fprintf(stderr,
+ "Failed to parse "
+ "--use-kerberos=desired|required|off: "
+ "Missing argument\n");
+ exit(1);
+ }
+
+ use_kerberos = lpcfg_parse_enum_vals("client use kerberos",
+ arg);
+ if (use_kerberos == INT_MIN) {
+ fprintf(stderr,
+ "Failed to parse "
+ "--use-kerberos=desired|required|off: "
+ "Invalid argument\n");
+ exit(1);
+ }
+
+ ok = cli_credentials_set_kerberos_state(creds,
+ use_kerberos,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set Kerberos state to %s!\n", arg);
+ exit(1);
+ }
+ break;
+ }
+ case OPT_USE_KERBEROS_CCACHE: {
+ const char *error_string = NULL;
+ int rc;
+
+ if (arg == NULL) {
+ fprintf(stderr,
+ "Failed to parse --use-krb5-ccache=CCACHE: "
+ "Missing argument\n");
+ exit(1);
+ }
+
+ ok = cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set Kerberos state to %s!\n", arg);
+ exit(1);
+ }
+
+ rc = cli_credentials_set_ccache(creds,
+ lp_ctx,
+ arg,
+ CRED_SPECIFIED,
+ &error_string);
+ if (rc != 0) {
+ fprintf(stderr,
+ "Error reading krb5 credentials cache: '%s'"
+ " - %s\n",
+ arg,
+ error_string);
+ exit(1);
+ }
+
+ skip_password_callback = true;
+ break;
+ }
+ case OPT_USE_WINBIND_CCACHE:
+ {
+ uint32_t gensec_features;
+
+ gensec_features = cli_credentials_get_gensec_features(creds);
+ gensec_features |= GENSEC_FEATURE_NTLM_CCACHE;
+
+ ok = cli_credentials_set_gensec_features(creds,
+ gensec_features,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set gensec feature!\n");
+ exit(1);
+ }
+
+ skip_password_callback = true;
+ break;
+ }
+ case OPT_CLIENT_PROTECTION: {
+ uint32_t gensec_features;
+ enum smb_signing_setting signing_state =
+ SMB_SIGNING_OFF;
+ enum smb_encryption_setting encryption_state =
+ SMB_ENCRYPTION_OFF;
+
+ if (arg == NULL) {
+ fprintf(stderr,
+ "Failed to parse "
+ "--client-protection=sign|encrypt|off: "
+ "Missing argument\n");
+ exit(1);
+ }
+
+ gensec_features =
+ cli_credentials_get_gensec_features(
+ creds);
+
+ if (strequal(arg, "off")) {
+ gensec_features &=
+ ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL);
+
+ signing_state = SMB_SIGNING_OFF;
+ encryption_state = SMB_ENCRYPTION_OFF;
+ } else if (strequal(arg, "sign")) {
+ gensec_features |= GENSEC_FEATURE_SIGN;
+
+ signing_state = SMB_SIGNING_REQUIRED;
+ encryption_state = SMB_ENCRYPTION_OFF;
+ } else if (strequal(arg, "encrypt")) {
+ gensec_features |= GENSEC_FEATURE_SEAL;
+
+ signing_state = SMB_SIGNING_REQUIRED;
+ encryption_state = SMB_ENCRYPTION_REQUIRED;
+ } else {
+ fprintf(stderr,
+ "Failed to parse --client-protection\n");
+ exit(1);
+ }
+
+ ok = cli_credentials_set_gensec_features(creds,
+ gensec_features,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set gensec feature!\n");
+ exit(1);
+ }
+
+ ok = cli_credentials_set_smb_signing(creds,
+ signing_state,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set smb signing!\n");
+ exit(1);
+ }
+
+ ok = cli_credentials_set_smb_encryption(creds,
+ encryption_state,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set smb encryption!\n");
+ exit(1);
+ }
+ break;
+ }
+ } /* switch */
+}
+
+static struct poptOption popt_common_credentials[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
+ .arg = (void *)popt_common_credentials_callback,
+ },
+ {
+ .longName = "user",
+ .shortName = 'U',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'U',
+ .descrip = "Set the network username",
+ .argDescrip = "[DOMAIN/]USERNAME[%PASSWORD]",
+ },
+ {
+ .longName = "no-pass",
+ .shortName = 'N',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'N',
+ .descrip = "Don't ask for a password",
+ },
+ {
+ .longName = "password",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_PASSWORD,
+ .descrip = "Password",
+ },
+ {
+ .longName = "pw-nt-hash",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_NT_HASH,
+ .descrip = "The supplied password is the NT hash",
+ },
+ {
+ .longName = "authentication-file",
+ .shortName = 'A',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'A',
+ .descrip = "Get the credentials from a file",
+ .argDescrip = "FILE",
+ },
+ {
+ .longName = "machine-pass",
+ .shortName = 'P',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'P',
+ .descrip = "Use stored machine account password",
+ },
+ {
+ .longName = "simple-bind-dn",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_SIMPLE_BIND_DN,
+ .descrip = "DN to use for a simple bind",
+ .argDescrip = "DN",
+ },
+ {
+ .longName = "use-kerberos",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_USE_KERBEROS,
+ .descrip = "Use Kerberos authentication",
+ .argDescrip = "desired|required|off",
+ },
+ {
+ .longName = "use-krb5-ccache",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_USE_KERBEROS_CCACHE,
+ .descrip = "Credentials cache location for Kerberos",
+ .argDescrip = "CCACHE",
+ },
+ {
+ .longName = "use-winbind-ccache",
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_USE_WINBIND_CCACHE,
+ .descrip = "Use the winbind ccache for authentication",
+ },
+ {
+ .longName = "client-protection",
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_CLIENT_PROTECTION,
+ .descrip = "Configure used protection for client connections",
+ .argDescrip = "sign|encrypt|off",
+ },
+ POPT_TABLEEND
+};
+
+/**********************************************************
+ * VERSION POPT
+ **********************************************************/
+
+static void popt_version_callback(poptContext ctx,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg,
+ const void *data)
+{
+ switch(opt->val) {
+ case 'V':
+ printf("Version %s\n", SAMBA_VERSION_STRING);
+ exit(0);
+ }
+}
+
+static struct poptOption popt_common_version[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK,
+ .arg = (void *)popt_version_callback,
+ },
+ {
+ .longName = "version",
+ .shortName = 'V',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'V',
+ .descrip = "Print version",
+ },
+ POPT_TABLEEND
+};
+
+/**********************************************************
+ * DAEMON POPT
+ **********************************************************/
+
+static void popt_daemon_callback(poptContext ctx,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg,
+ const void *data)
+{
+ switch(opt->val) {
+ case OPT_DAEMON:
+ cmdline_daemon_cfg.daemon = true;
+ break;
+ case OPT_INTERACTIVE:
+ cmdline_daemon_cfg.interactive = true;
+ cmdline_daemon_cfg.fork = false;
+ break;
+ case OPT_FORK:
+ cmdline_daemon_cfg.fork = false;
+ break;
+ case OPT_NO_PROCESS_GROUP:
+ cmdline_daemon_cfg.no_process_group = true;
+ break;
+ }
+}
+
+static struct poptOption popt_common_daemon[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK,
+ .arg = (void *)popt_daemon_callback
+ },
+ {
+ .longName = "daemon",
+ .shortName = 'D',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = OPT_DAEMON,
+ .descrip = "Become a daemon (default)" ,
+ },
+ {
+ .longName = "interactive",
+ .shortName = 'i',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = OPT_INTERACTIVE,
+ .descrip = "Run interactive (not a daemon) and log to stdout",
+ },
+ {
+ .longName = "foreground",
+ .shortName = 'F',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = OPT_FORK,
+ .descrip = "Run daemon in foreground (for daemontools, etc.)",
+ },
+ {
+ .longName = "no-process-group",
+ .shortName = '\0',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = OPT_NO_PROCESS_GROUP,
+ .descrip = "Don't create a new process group" ,
+ },
+ POPT_TABLEEND
+};
+
+/**********************************************************
+ * LEGACY S3 POPT
+ **********************************************************/
+
+static void popt_legacy_s3_callback(poptContext ctx,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg,
+ const void *data)
+{
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ bool ok;
+
+ switch(opt->val) {
+ case 'k':
+ fprintf(stderr,
+ "WARNING: The option -k|--kerberos is deprecated!\n");
+
+ ok = cli_credentials_set_kerberos_state(creds,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set Kerberos state to %s!\n", arg);
+ exit(1);
+ }
+
+ skip_password_callback = true;
+ break;
+ }
+}
+
+/* We allow '-k yes' too. */
+static struct poptOption popt_legacy_s3[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK,
+ .arg = (void *)popt_legacy_s3_callback,
+ },
+ {
+ .longName = "kerberos",
+ .shortName = 'k',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'k',
+ .descrip = "DEPRECATED: Migrate to --use-kerberos",
+ },
+ POPT_TABLEEND
+};
+
+/**********************************************************
+ * LEGACY S4 POPT
+ **********************************************************/
+
+static void popt_legacy_s4_callback(poptContext ctx,
+ enum poptCallbackReason reason,
+ const struct poptOption *opt,
+ const char *arg,
+ const void *data)
+{
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ bool ok;
+
+ switch(opt->val) {
+ case 'k': {
+ enum credentials_use_kerberos use_kerberos =
+ CRED_USE_KERBEROS_REQUIRED;
+
+ fprintf(stderr,
+ "WARNING: The option -k|--kerberos is deprecated!\n");
+
+ if (arg != NULL) {
+ if (strcasecmp_m(arg, "yes") == 0) {
+ use_kerberos = CRED_USE_KERBEROS_REQUIRED;
+ } else if (strcasecmp_m(arg, "no") == 0) {
+ use_kerberos = CRED_USE_KERBEROS_DISABLED;
+ } else {
+ fprintf(stderr,
+ "Error parsing -k %s. Should be "
+ "-k [yes|no]\n",
+ arg);
+ exit(1);
+ }
+ }
+
+ ok = cli_credentials_set_kerberos_state(creds,
+ use_kerberos,
+ CRED_SPECIFIED);
+ if (!ok) {
+ fprintf(stderr,
+ "Failed to set Kerberos state to %s!\n", arg);
+ exit(1);
+ }
+
+ break;
+ }
+ }
+}
+
+static struct poptOption popt_legacy_s4[] = {
+ {
+ .argInfo = POPT_ARG_CALLBACK,
+ .arg = (void *)popt_legacy_s4_callback,
+ },
+ {
+ .longName = "kerberos",
+ .shortName = 'k',
+ .argInfo = POPT_ARG_STRING,
+ .val = 'k',
+ .descrip = "DEPRECATED: Migrate to --use-kerberos",
+ },
+ POPT_TABLEEND
+};
+
+struct poptOption *samba_cmdline_get_popt(enum smb_cmdline_popt_options opt)
+{
+ switch (opt) {
+ case SAMBA_CMDLINE_POPT_OPT_DEBUG_ONLY:
+ return popt_common_debug;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_OPTION_ONLY:
+ return popt_common_option;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_CONFIG_ONLY:
+ return popt_common_config;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_SAMBA:
+ return popt_common_samba;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_CONNECTION:
+ return popt_common_connection;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_CREDENTIALS:
+ return popt_common_credentials;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_VERSION:
+ return popt_common_version;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_DAEMON:
+ return popt_common_daemon;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_SAMBA_LDB:
+ return popt_common_samba_ldb;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_LEGACY_S3:
+ return popt_legacy_s3;
+ break;
+ case SAMBA_CMDLINE_POPT_OPT_LEGACY_S4:
+ return popt_legacy_s4;
+ break;
+ }
+
+ /* Never reached */
+ return NULL;
+}
diff --git a/lib/cmdline/cmdline.h b/lib/cmdline/cmdline.h
new file mode 100644
index 0000000..ce5dd23
--- /dev/null
+++ b/lib/cmdline/cmdline.h
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2020 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CMDLINE_H
+#define _CMDLINE_H
+
+#include "auth/credentials/credentials.h"
+#include <popt.h>
+
+#ifndef POPT_TABLEEND
+#define POPT_TABLEEND { \
+ .longName = NULL, \
+ .shortName = 0, \
+ .argInfo = 0, \
+ .arg = NULL, \
+ .val = 0, \
+ .descrip = NULL, \
+ .argDescrip = NULL }
+#endif
+
+enum samba_cmdline_config_type {
+ SAMBA_CMDLINE_CONFIG_NONE = 0,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ SAMBA_CMDLINE_CONFIG_SERVER,
+};
+
+enum smb_cmdline_popt_options {
+ SAMBA_CMDLINE_POPT_OPT_DEBUG_ONLY = 1,
+ SAMBA_CMDLINE_POPT_OPT_OPTION_ONLY,
+ SAMBA_CMDLINE_POPT_OPT_CONFIG_ONLY,
+ SAMBA_CMDLINE_POPT_OPT_SAMBA,
+ SAMBA_CMDLINE_POPT_OPT_CONNECTION,
+ SAMBA_CMDLINE_POPT_OPT_CREDENTIALS,
+ SAMBA_CMDLINE_POPT_OPT_VERSION,
+ SAMBA_CMDLINE_POPT_OPT_DAEMON,
+ SAMBA_CMDLINE_POPT_OPT_SAMBA_LDB,
+ SAMBA_CMDLINE_POPT_OPT_LEGACY_S3,
+ SAMBA_CMDLINE_POPT_OPT_LEGACY_S4,
+};
+
+struct samba_cmdline_daemon_cfg {
+ bool daemon;
+ bool interactive;
+ bool fork;
+ bool no_process_group;
+};
+
+/**
+ * @brief Initialize the commandline interface for parsing options.
+ *
+ * This initializes the interface for parsing options given on the command
+ * line. It sets up the loadparm and client credentials contexts.
+ * The function will also setup fault handler, set logging to STDERR by
+ * default, setup talloc logging and the panic handler.
+ *
+ * The function also setups a callback for loading the smb.conf file, the
+ * config file will be parsed after the commandline options have been parsed
+ * by popt. This is done by one of the following options parser:
+ *
+ * POPT_COMMON_DEBUG_ONLY
+ * POPT_COMMON_OPTION_ONLY
+ * POPT_COMMON_CONFIG_ONLY
+ * POPT_COMMON_SAMBA
+ *
+ * @param[in] mem_ctx The talloc memory context to use for allocating memory.
+ * This should be a long living context till the client
+ * exits.
+ *
+ * @param[in] require_smbconf Whether the smb.conf file is required to be
+ * present or not?
+ *
+ * @return true on success, false if an error occurred.
+ */
+bool samba_cmdline_init(TALLOC_CTX *mem_ctx,
+ enum samba_cmdline_config_type config_type,
+ bool require_smbconf);
+
+/**
+ * @brief Get a pointer of loadparm context used for the command line interface.
+ *
+ * @return The loadparm context.
+ */
+struct loadparm_context *samba_cmdline_get_lp_ctx(void);
+
+/**
+ * @brief Get the client credentials of the command line interface.
+ *
+ * @return A pointer to the client credentials.
+ */
+struct cli_credentials *samba_cmdline_get_creds(void);
+
+/**
+ * @brief Get a pointer to the poptOption for the given option section.
+ *
+ * You should not directly use this function, but the macros.
+ *
+ * @param[in] opt The options to retrieve.
+ *
+ * @return A pointer to the poptOption array.
+ *
+ * @see POPT_COMMON_DEBUG_ONLY
+ * @see POPT_COMMON_OPTION_ONLY
+ * @see POPT_COMMON_CONFIG_ONLY
+ * @see POPT_COMMON_SAMBA
+ * @see POPT_COMMON_CONNECTION
+ * @see POPT_COMMON_CREDENTIALS
+ * @see POPT_COMMON_VERSION
+ */
+struct poptOption *samba_cmdline_get_popt(enum smb_cmdline_popt_options opt);
+
+/**
+ * @brief Get a pointer to the poptOptions for daemons
+ *
+ * @return A pointer to the daemon options
+ *
+ * @see POPT_COMMON_DAEMON
+ */
+struct samba_cmdline_daemon_cfg *samba_cmdline_get_daemon_cfg(void);
+
+void samba_cmdline_set_machine_account_fn(
+ NTSTATUS (*fn) (struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx));
+
+/**
+ * @brief Burn secrets on the command line.
+ *
+ * This function removes secrets from the command line so we don't leak e.g.
+ * passwords on 'ps aux' output.
+ *
+ * It should be called after processing the options and you should pass down
+ * argv from main().
+ *
+ * @param[in] argc The number of arguments.
+ *
+ * @param[in] argv[] The argument array we should remove secrets from.
+ *
+ * @return true if a password was removed, false otherwise.
+ */
+bool samba_cmdline_burn(int argc, char *argv[]);
+
+/**
+ * @brief Sanity check the command line options.
+ *
+ * This checks for duplicates in short and long options.
+ *
+ * @param[in] opts The options array to check.
+ *
+ * @return true if valid, false otherwise.
+ */
+bool samba_cmdline_sanity_check(const struct poptOption *opts);
+
+/**
+ * @brief This is a wrapper for the poptGetContext() which initializes the popt
+ * context.
+ *
+ * If Samba is build in developer mode, this will call
+ * samba_cmdline_sanity_check() before poptGetContext().
+ *
+ * @param[in] name The context name (usually argv[0] program name or
+ * getprogname())
+ *
+ * @param[in] argc Number of arguments
+ *
+ * @param[in] argv The argument array
+ *
+ * @param[in] options The address of popt option table
+ *
+ * @param[in] flags The OR'd POPT_CONTEXT_* bits
+ *
+ * @return The initialized popt context or NULL on error.
+ */
+poptContext samba_popt_get_context(const char * name,
+ int argc, const char ** argv,
+ const struct poptOption * options,
+ unsigned int flags);
+
+/**
+ * @brief A popt structure for common debug options only.
+ */
+#define POPT_COMMON_DEBUG_ONLY { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_DEBUG_ONLY), \
+ .val = 0, \
+ .descrip = "Common debug options:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for --option only.
+ */
+#define POPT_COMMON_OPTION_ONLY { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_OPTION_ONLY), \
+ .val = 0, \
+ .descrip = "Options:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for --configfile only.
+ */
+#define POPT_COMMON_CONFIG_ONLY { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_CONFIG_ONLY), \
+ .val = 0, \
+ .descrip = "Config file:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for common samba options.
+ */
+#define POPT_COMMON_SAMBA { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_SAMBA), \
+ .val = 0, \
+ .descrip = "Common Samba options:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for connection options.
+ */
+#define POPT_COMMON_CONNECTION { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_CONNECTION), \
+ .val = 0, \
+ .descrip = "Connection options:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for credential options.
+ */
+#define POPT_COMMON_CREDENTIALS { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_CREDENTIALS), \
+ .val = 0, \
+ .descrip = "Credential options:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for version options.
+ */
+#define POPT_COMMON_VERSION { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_VERSION), \
+ .val = 0, \
+ .descrip = "Version options:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for daemon options.
+ */
+#define POPT_COMMON_DAEMON { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_DAEMON), \
+ .val = 0, \
+ .descrip = "Daemon options:", \
+ .argDescrip = NULL },
+
+/**
+ * @brief A popt structure for common samba options.
+ */
+#define POPT_COMMON_SAMBA_LDB { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_SAMBA_LDB), \
+ .val = 0, \
+ .descrip = "Common Samba options:", \
+ .argDescrip = NULL },
+
+/* TODO Get rid of me! */
+#define POPT_LEGACY_S3 { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_LEGACY_S3), \
+ .val = 0, \
+ .descrip = "Deprecated legacy options:", \
+ .argDescrip = NULL },
+
+/* TODO Get rid of me! */
+#define POPT_LEGACY_S4 { \
+ .longName = NULL, \
+ .shortName = '\0', \
+ .argInfo = POPT_ARG_INCLUDE_TABLE, \
+ .arg = samba_cmdline_get_popt(SAMBA_CMDLINE_POPT_OPT_LEGACY_S4), \
+ .val = 0, \
+ .descrip = "Deprecated legacy options:", \
+ .argDescrip = NULL },
+
+#endif /* _CMDLINE_H */
diff --git a/lib/cmdline/cmdline_private.h b/lib/cmdline/cmdline_private.h
new file mode 100644
index 0000000..e2d4e95
--- /dev/null
+++ b/lib/cmdline/cmdline_private.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2020 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CMDLINE_PRIVATE_H
+#define _CMDLINE_PRIVATE_H
+
+#include "lib/cmdline/cmdline.h"
+
+enum {
+ OPT_OPTION = 0x1000000,
+ OPT_NETBIOS_SCOPE,
+ OPT_LEAK_REPORT,
+ OPT_LEAK_REPORT_FULL,
+ OPT_DEBUG_STDOUT,
+ OPT_CONFIGFILE,
+ OPT_SIMPLE_BIND_DN,
+ OPT_PASSWORD,
+ OPT_NT_HASH,
+ OPT_USE_KERBEROS,
+ OPT_USE_KERBEROS_CCACHE,
+ OPT_USE_WINBIND_CCACHE,
+ OPT_CLIENT_PROTECTION,
+ OPT_DAEMON,
+ OPT_INTERACTIVE,
+ OPT_FORK,
+ OPT_NO_PROCESS_GROUP,
+};
+
+typedef bool (*samba_cmdline_load_config)(void);
+
+/**
+ * @internal
+ *
+ * @brief Initialize the commandline interface for parsing options.
+ *
+ * This the common function to initialize the command line interface. This
+ * initializes:
+ *
+ * - Crash setup
+ * - logging system sending logs to stdout
+ * - talloc leak reporting
+ *
+ * @param[in] mem_ctx The talloc memory context to use for allocating memory.
+ * This should be a long living context till the client
+ * exits.
+ *
+ * @return true on success, false if an error occurred.
+ */
+bool samba_cmdline_init_common(TALLOC_CTX *mem_ctx);
+
+/**
+ * @brief Set the callback for loading the smb.conf file.
+ *
+ * This is needed as sourc3 and source4 have different code for loading the
+ * smb.conf file.
+ *
+ * @param[in] fn The callback to load the smb.conf file.
+ *
+ * @return true on success, false if an error occurred.
+ */
+bool samba_cmdline_set_load_config_fn(samba_cmdline_load_config fn);
+
+/**
+ * @internal
+ *
+ * @brief Set the talloc context for the command line interface.
+ *
+ * This is stored as a static pointer.
+ *
+ * @param[in] mem_ctx The talloc memory context.
+ *
+ * @return true on success, false if an error occurred.
+ */
+bool samba_cmdline_set_talloc_ctx(TALLOC_CTX *mem_ctx);
+
+/**
+ * @internal
+ *
+ * @brief Get the talloc context for the cmdline interface.
+ *
+ * @return A talloc context.
+ */
+TALLOC_CTX *samba_cmdline_get_talloc_ctx(void);
+
+/**
+ * @internal
+ *
+ * @brief Set the loadparm context for the command line interface.
+ *
+ * @param[in] lp_ctx The loadparm context to use.
+ *
+ * @return true on success, false if an error occurred.
+ */
+bool samba_cmdline_set_lp_ctx(struct loadparm_context *lp_ctx);
+
+/**
+ * @internal
+ *
+ * @brief Set the client credentials for the commandline interface.
+ *
+ * @param[in] creds The client credentials to use.
+ *
+ * @return true on success, false if an error occurred.
+ */
+bool samba_cmdline_set_creds(struct cli_credentials *creds);
+
+#endif /* _CMDLINE_PRIVATE_H */
diff --git a/lib/cmdline/cmdline_s3.c b/lib/cmdline/cmdline_s3.c
new file mode 100644
index 0000000..6e2c154
--- /dev/null
+++ b/lib/cmdline/cmdline_s3.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "lib/replace/replace.h"
+#include <talloc.h>
+#include "lib/param/param.h"
+#include "lib/util/debug.h"
+#include "lib/util/fault.h"
+#include "source3/param/loadparm.h"
+#include "dynconfig/dynconfig.h"
+#include "source3/lib/interface.h"
+#include "auth/credentials/credentials.h"
+#include "dynconfig/dynconfig.h"
+#include "cmdline_private.h"
+#include "source3/include/secrets.h"
+
+static bool _require_smbconf;
+static enum samba_cmdline_config_type _config_type;
+
+static bool _samba_cmdline_load_config_s3(void)
+{
+ struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
+ const char *config_file = NULL;
+ bool ok = false;
+
+ /* Load smb conf */
+ config_file = lpcfg_configfile(lp_ctx);
+ if (config_file == NULL) {
+ if (is_default_dyn_CONFIGFILE()) {
+ const char *env = getenv("SMB_CONF_PATH");
+ if (env != NULL && strlen(env) > 0) {
+ set_dyn_CONFIGFILE(env);
+ }
+ }
+ }
+
+ config_file = get_dyn_CONFIGFILE();
+
+ switch (_config_type) {
+ case SAMBA_CMDLINE_CONFIG_NONE:
+ return true;
+ case SAMBA_CMDLINE_CONFIG_CLIENT:
+ ok = lp_load_client(config_file);
+ break;
+ case SAMBA_CMDLINE_CONFIG_SERVER:
+ {
+ const struct samba_cmdline_daemon_cfg *cmdline_daemon_cfg =
+ samba_cmdline_get_daemon_cfg();
+
+ if (!cmdline_daemon_cfg->interactive) {
+ setup_logging(getprogname(), DEBUG_FILE);
+ }
+
+ ok = lp_load_global(config_file);
+ break;
+ }
+ }
+
+ if (!ok) {
+ fprintf(stderr,
+ "Can't load %s - run testparm to debug it\n",
+ config_file);
+
+ if (_require_smbconf) {
+ return false;
+ }
+ }
+
+ load_interfaces();
+
+ return true;
+}
+
+static NTSTATUS _samba_cmd_set_machine_account_s3(
+ struct cli_credentials *cred,
+ struct loadparm_context *lp_ctx)
+{
+ struct db_context *db_ctx = secrets_db_ctx();
+ NTSTATUS status;
+
+ if (db_ctx == NULL) {
+ DBG_WARNING("failed to open secrets.tdb to obtain our "
+ "trust credentials for %s\n",
+ lpcfg_workgroup(lp_ctx));;
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = cli_credentials_set_machine_account_db_ctx(
+ cred, lp_ctx, db_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("cli_credentials_set_machine_account_db_ctx "
+ "failed: %s\n",
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+bool samba_cmdline_init(TALLOC_CTX *mem_ctx,
+ enum samba_cmdline_config_type config_type,
+ bool require_smbconf)
+{
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ bool ok;
+
+ ok = samba_cmdline_init_common(mem_ctx);
+ if (!ok) {
+ return false;
+ }
+
+ lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ return false;
+ }
+ ok = samba_cmdline_set_lp_ctx(lp_ctx);
+ if (!ok) {
+ return false;
+ }
+
+ _require_smbconf = require_smbconf;
+ _config_type = config_type;
+
+ creds = cli_credentials_init(mem_ctx);
+ if (creds == NULL) {
+ return false;
+ }
+ ok = samba_cmdline_set_creds(creds);
+ if (!ok) {
+ return false;
+ }
+
+ samba_cmdline_set_load_config_fn(_samba_cmdline_load_config_s3);
+ samba_cmdline_set_machine_account_fn(
+ _samba_cmd_set_machine_account_s3);
+
+ return true;
+}
diff --git a/lib/cmdline/cmdline_s4.c b/lib/cmdline/cmdline_s4.c
new file mode 100644
index 0000000..f8be4ed
--- /dev/null
+++ b/lib/cmdline/cmdline_s4.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "lib/replace/replace.h"
+#include <talloc.h>
+#include "lib/param/param.h"
+#include "lib/util/debug.h"
+#include "lib/util/fault.h"
+#include "auth/credentials/credentials.h"
+#include "dynconfig/dynconfig.h"
+#include "cmdline_private.h"
+
+static bool _require_smbconf;
+static enum samba_cmdline_config_type _config_type;
+
+static bool _samba_cmdline_load_config_s4(void)
+{
+ struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
+ const char *config_file = NULL;
+ const struct samba_cmdline_daemon_cfg *cmdline_daemon_cfg = \
+ samba_cmdline_get_daemon_cfg();
+ bool ok;
+
+ /* Load smb conf */
+ config_file = lpcfg_configfile(lp_ctx);
+ if (config_file == NULL) {
+ if (is_default_dyn_CONFIGFILE()) {
+ const char *env = getenv("SMB_CONF_PATH");
+ if (env != NULL && strlen(env) > 0) {
+ set_dyn_CONFIGFILE(env);
+ }
+ }
+ }
+
+ switch (_config_type) {
+ case SAMBA_CMDLINE_CONFIG_SERVER:
+ if (!cmdline_daemon_cfg->interactive) {
+ setup_logging(getprogname(), DEBUG_FILE);
+ }
+ break;
+ default:
+ break;
+ }
+
+ config_file = get_dyn_CONFIGFILE();
+ ok = lpcfg_load(lp_ctx, config_file);
+ if (!ok) {
+ fprintf(stderr,
+ "Can't load %s - run testparm to debug it\n",
+ config_file);
+
+ if (_require_smbconf) {
+ return false;
+ }
+ }
+
+ switch (_config_type) {
+ case SAMBA_CMDLINE_CONFIG_SERVER:
+ /*
+ * We need to setup_logging *again* to ensure multi-file
+ * logging is set up as specified in smb.conf.
+ */
+ if (!cmdline_daemon_cfg->interactive) {
+ setup_logging(getprogname(), DEBUG_FILE);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool samba_cmdline_init(TALLOC_CTX *mem_ctx,
+ enum samba_cmdline_config_type config_type,
+ bool require_smbconf)
+{
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ bool ok;
+
+ ok = samba_cmdline_init_common(mem_ctx);
+ if (!ok) {
+ return false;
+ }
+
+ lp_ctx = loadparm_init_global(false);
+ if (lp_ctx == NULL) {
+ return false;
+ }
+
+ ok = samba_cmdline_set_lp_ctx(lp_ctx);
+ if (!ok) {
+ return false;
+ }
+ _require_smbconf = require_smbconf;
+ _config_type = config_type;
+
+ creds = cli_credentials_init(mem_ctx);
+ if (creds == NULL) {
+ return false;
+ }
+ ok = samba_cmdline_set_creds(creds);
+ if (!ok) {
+ return false;
+ }
+
+ samba_cmdline_set_load_config_fn(_samba_cmdline_load_config_s4);
+
+ return true;
+}
diff --git a/lib/cmdline/tests/test_cmdline.c b/lib/cmdline/tests/test_cmdline.c
new file mode 100644
index 0000000..16dd09c
--- /dev/null
+++ b/lib/cmdline/tests/test_cmdline.c
@@ -0,0 +1,97 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2018-2019 Andreas Schneider <asn@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "lib/cmdline/cmdline.h"
+
+static void torture_cmdline_sanity_check_good(void **state)
+{
+ bool ok;
+ struct poptOption long_options_good[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_LEGACY_S3
+ POPT_TABLEEND
+ };
+
+ ok = samba_cmdline_sanity_check(long_options_good);
+ assert_true(ok);
+}
+
+static void torture_cmdline_sanity_check_bad(void **state)
+{
+ bool ok;
+
+ struct poptOption long_options_bad[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ ok = samba_cmdline_sanity_check(long_options_bad);
+ assert_false(ok);
+}
+
+static void torture_cmdline_burn(void **state)
+{
+ char arg1[] = "-U Administrator%secret";
+ char arg2[] = "--user=Administrator%secret";
+ char arg3[] = "--user=Administrator%super%secret";
+ char arg4[] = "--password=super%secret";
+
+ char *argv[] = { arg1, arg2, arg3, arg4, NULL };
+ int argc = 4;
+
+ samba_cmdline_burn(argc, argv);
+
+ assert_string_equal(arg1, "-U Administrator");
+ assert_string_equal(arg2, "--user=Administrator");
+ assert_string_equal(arg3, "--user=Administrator");
+ assert_string_equal(arg4, "--password");
+}
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(torture_cmdline_sanity_check_good),
+ cmocka_unit_test(torture_cmdline_sanity_check_bad),
+ cmocka_unit_test(torture_cmdline_burn),
+ };
+
+ if (argc == 2) {
+ cmocka_set_test_filter(argv[1]);
+ }
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ rc = cmocka_run_group_tests(tests, NULL, NULL);
+
+ return rc;
+}
diff --git a/lib/cmdline/wscript b/lib/cmdline/wscript
new file mode 100644
index 0000000..01ead85
--- /dev/null
+++ b/lib/cmdline/wscript
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+
+import os
+import sys
+from waflib import Logs
+
+def build(bld):
+ bld.SAMBA_LIBRARY('cmdline',
+ source='''
+ cmdline.c
+ closefrom_except.c
+ ''',
+ deps='''
+ talloc
+ cli_smb_common
+ samba-hostconfig
+ samba-credentials
+ CREDENTIALS_CMDLINE
+ popt
+ ''',
+ private_library=True)
+
+ bld.SAMBA_SUBSYSTEM('CMDLINE_S3',
+ source='cmdline_s3.c',
+ deps='cmdline secrets3')
+
+ bld.SAMBA_SUBSYSTEM('CMDLINE_S4',
+ source='cmdline_s4.c',
+ deps='cmdline')
+
+ bld.SAMBA_BINARY('test_cmdline',
+ source='tests/test_cmdline.c',
+ deps='cmocka CMDLINE_S3 LOADPARM_CTX',
+ local_include=False,
+ for_selftest=True)