diff options
Diffstat (limited to 'lib/cmdline')
-rw-r--r-- | lib/cmdline/closefrom_except.c | 93 | ||||
-rw-r--r-- | lib/cmdline/closefrom_except.h | 29 | ||||
-rw-r--r-- | lib/cmdline/cmdline.c | 1400 | ||||
-rw-r--r-- | lib/cmdline/cmdline.h | 317 | ||||
-rw-r--r-- | lib/cmdline/cmdline_private.h | 121 | ||||
-rw-r--r-- | lib/cmdline/cmdline_s3.c | 152 | ||||
-rw-r--r-- | lib/cmdline/cmdline_s4.c | 125 | ||||
-rw-r--r-- | lib/cmdline/tests/test_cmdline.c | 95 | ||||
-rw-r--r-- | lib/cmdline/wscript | 35 |
9 files changed, 2367 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..9f4e964 --- /dev/null +++ b/lib/cmdline/cmdline.c @@ -0,0 +1,1400 @@ +/* + * 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 <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; +} + +void samba_cmdline_burn(int argc, char *argv[]) +{ + 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; + } + + 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, "--password", 10) == 0) { + ulen = 10; + 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; + } + } +} + +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 + 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. The 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..e254a1d --- /dev/null +++ b/lib/cmdline/cmdline.h @@ -0,0 +1,317 @@ +/* + * 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 Wether the smb.conf file is required to be + * present or not? + * + * @return true on success, false if an error occured. + */ +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. + */ +void 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..b1584e0 --- /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 sening 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 occured. + */ +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 occured. + */ +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 occured. + */ +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 occured. + */ +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 occured. + */ +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..5148243 --- /dev/null +++ b/lib/cmdline/tests/test_cmdline.c @@ -0,0 +1,95 @@ +/* + * 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 "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) |