1/*
2   Unix SMB/CIFS implementation.
3   Common popt routines
4
5   Copyright (C) Tim Potter 2001,2002
6   Copyright (C) Jelmer Vernooij 2002,2003
7   Copyright (C) James Peach 2006
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "includes.h"
24
25/* Handle command line options:
26 *		-d,--debuglevel
27 *		-s,--configfile
28 *		-O,--socket-options
29 *		-V,--version
30 *		-l,--log-base
31 *		-n,--netbios-name
32 *		-W,--workgroup
33 *		-i,--scope
34 */
35
36extern bool AllowDebugChange;
37extern bool override_logfile;
38
39static void set_logfile(poptContext con, const char * arg)
40{
41
42	char *lfile = NULL;
43	const char *pname;
44
45	/* Find out basename of current program */
46	pname = strrchr_m(poptGetInvocationName(con),'/');
47
48	if (!pname)
49		pname = poptGetInvocationName(con);
50	else
51		pname++;
52
53	if (asprintf(&lfile, "%s/log.%s", arg, pname) < 0) {
54		return;
55	}
56	lp_set_logfile(lfile);
57	SAFE_FREE(lfile);
58}
59
60static bool PrintSambaVersionString;
61
62static void popt_s3_talloc_log_fn(const char *message)
63{
64	DEBUG(0,("%s", message));
65}
66
67static void popt_common_callback(poptContext con,
68			   enum poptCallbackReason reason,
69			   const struct poptOption *opt,
70			   const char *arg, const void *data)
71{
72
73	if (reason == POPT_CALLBACK_REASON_PRE) {
74		set_logfile(con, get_dyn_LOGFILEBASE());
75		talloc_set_log_fn(popt_s3_talloc_log_fn);
76		talloc_set_abort_fn(smb_panic);
77		return;
78	}
79
80	if (reason == POPT_CALLBACK_REASON_POST) {
81
82		if (PrintSambaVersionString) {
83			printf( "Version %s\n", samba_version_string());
84			exit(0);
85		}
86
87		if (is_default_dyn_CONFIGFILE()) {
88			if(getenv("SMB_CONF_PATH")) {
89				set_dyn_CONFIGFILE(getenv("SMB_CONF_PATH"));
90			}
91		}
92
93		/* Further 'every Samba program must do this' hooks here. */
94		return;
95	}
96
97	switch(opt->val) {
98	case 'd':
99		if (arg) {
100			debug_parse_levels(arg);
101			AllowDebugChange = False;
102		}
103		break;
104
105	case 'V':
106		PrintSambaVersionString = True;
107		break;
108
109	case 'O':
110		if (arg) {
111			lp_do_parameter(-1, "socket options", arg);
112		}
113		break;
114
115	case 's':
116		if (arg) {
117			set_dyn_CONFIGFILE(arg);
118		}
119		break;
120
121	case 'n':
122		if (arg) {
123			set_global_myname(arg);
124		}
125		break;
126
127	case 'l':
128		if (arg) {
129			set_logfile(con, arg);
130			override_logfile = True;
131			set_dyn_LOGFILEBASE(arg);
132		}
133		break;
134
135	case 'i':
136		if (arg) {
137			  set_global_scope(arg);
138		}
139		break;
140
141	case 'W':
142		if (arg) {
143			set_global_myworkgroup(arg);
144		}
145		break;
146	}
147}
148
149struct poptOption popt_common_connection[] = {
150	{ NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
151	{ "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
152	  "SOCKETOPTIONS" },
153	{ "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
154	{ "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
155	{ "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
156
157	POPT_TABLEEND
158};
159
160struct poptOption popt_common_samba[] = {
161	{ NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, (void *)popt_common_callback },
162	{ "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
163	{ "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternate configuration file", "CONFIGFILE" },
164	{ "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Base name for log files", "LOGFILEBASE" },
165	{ "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
166	POPT_TABLEEND
167};
168
169struct poptOption popt_common_configfile[] = {
170	{ NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, (void *)popt_common_callback },
171	{ "configfile", 0, POPT_ARG_STRING, NULL, 's', "Use alternate configuration file", "CONFIGFILE" },
172	POPT_TABLEEND
173};
174
175struct poptOption popt_common_version[] = {
176	{ NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_POST, (void *)popt_common_callback },
177	{ "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
178	POPT_TABLEEND
179};
180
181struct poptOption popt_common_debuglevel[] = {
182	{ NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
183	{ "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
184	POPT_TABLEEND
185};
186
187
188/* Handle command line options:
189 *		--sbindir
190 *		--bindir
191 *		--swatdir
192 *		--lmhostsfile
193 *		--libdir
194 *		--modulesdir
195 *		--shlibext
196 *		--lockdir
197 *		--statedir
198 *		--cachedir
199 *		--piddir
200 *		--smb-passwd-file
201 *		--private-dir
202 */
203
204enum dyn_item{
205	DYN_SBINDIR = 1,
206	DYN_BINDIR,
207	DYN_SWATDIR,
208	DYN_LMHOSTSFILE,
209	DYN_LIBDIR,
210	DYN_MODULESDIR,
211	DYN_SHLIBEXT,
212	DYN_LOCKDIR,
213	DYN_STATEDIR,
214	DYN_CACHEDIR,
215	DYN_PIDDIR,
216	DYN_SMB_PASSWD_FILE,
217	DYN_PRIVATE_DIR,
218};
219
220
221static void popt_dynconfig_callback(poptContext con,
222			   enum poptCallbackReason reason,
223			   const struct poptOption *opt,
224			   const char *arg, const void *data)
225{
226
227	switch (opt->val) {
228	case DYN_SBINDIR:
229		if (arg) {
230			set_dyn_SBINDIR(arg);
231		}
232		break;
233
234	case DYN_BINDIR:
235		if (arg) {
236			set_dyn_BINDIR(arg);
237		}
238		break;
239
240	case DYN_SWATDIR:
241		if (arg) {
242			set_dyn_SWATDIR(arg);
243		}
244		break;
245
246	case DYN_LMHOSTSFILE:
247		if (arg) {
248			set_dyn_LMHOSTSFILE(arg);
249		}
250		break;
251
252	case DYN_LIBDIR:
253		if (arg) {
254			set_dyn_LIBDIR(arg);
255		}
256		break;
257
258	case DYN_MODULESDIR:
259		if (arg) {
260			set_dyn_MODULESDIR(arg);
261		}
262		break;
263
264	case DYN_SHLIBEXT:
265		if (arg) {
266			set_dyn_SHLIBEXT(arg);
267		}
268		break;
269
270	case DYN_LOCKDIR:
271		if (arg) {
272			set_dyn_LOCKDIR(arg);
273		}
274		break;
275
276	case DYN_STATEDIR:
277		if (arg) {
278			set_dyn_STATEDIR(arg);
279		}
280		break;
281
282	case DYN_CACHEDIR:
283		if (arg) {
284			set_dyn_CACHEDIR(arg);
285		}
286		break;
287
288	case DYN_PIDDIR:
289		if (arg) {
290			set_dyn_PIDDIR(arg);
291		}
292		break;
293
294	case DYN_SMB_PASSWD_FILE:
295		if (arg) {
296			set_dyn_SMB_PASSWD_FILE(arg);
297		}
298		break;
299
300	case DYN_PRIVATE_DIR:
301		if (arg) {
302			set_dyn_PRIVATE_DIR(arg);
303		}
304		break;
305
306	}
307}
308
309const struct poptOption popt_common_dynconfig[] = {
310
311	{ NULL, '\0', POPT_ARG_CALLBACK, (void *)popt_dynconfig_callback },
312
313	{ "sbindir", '\0' , POPT_ARG_STRING, NULL, DYN_SBINDIR,
314	    "Path to sbin directory", "SBINDIR" },
315	{ "bindir", '\0' , POPT_ARG_STRING, NULL, DYN_BINDIR,
316	    "Path to bin directory", "BINDIR" },
317	{ "swatdir", '\0' , POPT_ARG_STRING, NULL, DYN_SWATDIR,
318	    "Path to SWAT installation directory", "SWATDIR" },
319	{ "lmhostsfile", '\0' , POPT_ARG_STRING, NULL, DYN_LMHOSTSFILE,
320	    "Path to lmhosts file", "LMHOSTSFILE" },
321	{ "libdir", '\0' , POPT_ARG_STRING, NULL, DYN_LIBDIR,
322	    "Path to shared library directory", "LIBDIR" },
323	{ "modulesdir", '\0' , POPT_ARG_STRING, NULL, DYN_MODULESDIR,
324	    "Path to shared modules directory", "MODULESDIR" },
325	{ "shlibext", '\0' , POPT_ARG_STRING, NULL, DYN_SHLIBEXT,
326	    "Shared library extension", "SHLIBEXT" },
327	{ "lockdir", '\0' , POPT_ARG_STRING, NULL, DYN_LOCKDIR,
328	    "Path to lock file directory", "LOCKDIR" },
329	{ "statedir", '\0' , POPT_ARG_STRING, NULL, DYN_STATEDIR,
330	    "Path to persistent state file directory", "STATEDIR" },
331	{ "cachedir", '\0' , POPT_ARG_STRING, NULL, DYN_CACHEDIR,
332	    "Path to temporary cache file directory", "CACHEDIR" },
333	{ "piddir", '\0' , POPT_ARG_STRING, NULL, DYN_PIDDIR,
334	    "Path to PID file directory", "PIDDIR" },
335	{ "smb-passwd-file", '\0' , POPT_ARG_STRING, NULL, DYN_SMB_PASSWD_FILE,
336	    "Path to smbpasswd file", "SMB_PASSWD_FILE" },
337	{ "private-dir", '\0' , POPT_ARG_STRING, NULL, DYN_PRIVATE_DIR,
338	    "Path to private data directory", "PRIVATE_DIR" },
339
340	POPT_TABLEEND
341};
342
343/****************************************************************************
344 * get a password from a a file or file descriptor
345 * exit on failure
346 * ****************************************************************************/
347
348static void get_password_file(struct user_auth_info *auth_info)
349{
350	int fd = -1;
351	char *p;
352	bool close_it = False;
353	char *spec = NULL;
354	char pass[128];
355
356	if ((p = getenv("PASSWD_FD")) != NULL) {
357		if (asprintf(&spec, "descriptor %s", p) < 0) {
358			return;
359		}
360		sscanf(p, "%d", &fd);
361		close_it = false;
362	} else if ((p = getenv("PASSWD_FILE")) != NULL) {
363		fd = sys_open(p, O_RDONLY, 0);
364		spec = SMB_STRDUP(p);
365		if (fd < 0) {
366			fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
367					spec, strerror(errno));
368			exit(1);
369		}
370		close_it = True;
371	}
372
373	if (fd < 0) {
374		fprintf(stderr, "fd = %d, < 0\n", fd);
375		exit(1);
376	}
377
378	for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
379		p && p - pass < sizeof(pass);) {
380		switch (read(fd, p, 1)) {
381		case 1:
382			if (*p != '\n' && *p != '\0') {
383				*++p = '\0'; /* advance p, and null-terminate pass */
384				break;
385			}
386		case 0:
387			if (p - pass) {
388				*p = '\0'; /* null-terminate it, just in case... */
389				p = NULL; /* then force the loop condition to become false */
390				break;
391			} else {
392				fprintf(stderr, "Error reading password from file %s: %s\n",
393						spec, "empty password\n");
394				SAFE_FREE(spec);
395				exit(1);
396			}
397
398		default:
399			fprintf(stderr, "Error reading password from file %s: %s\n",
400					spec, strerror(errno));
401			SAFE_FREE(spec);
402			exit(1);
403		}
404	}
405	SAFE_FREE(spec);
406
407	set_cmdline_auth_info_password(auth_info, pass);
408	if (close_it) {
409		close(fd);
410	}
411}
412
413static void get_credentials_file(struct user_auth_info *auth_info,
414				 const char *file)
415{
416	XFILE *auth;
417	fstring buf;
418	uint16 len = 0;
419	char *ptr, *val, *param;
420
421	if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
422	{
423		/* fail if we can't open the credentials file */
424		d_printf("ERROR: Unable to open credentials file!\n");
425		exit(-1);
426	}
427
428	while (!x_feof(auth))
429	{
430		/* get a line from the file */
431		if (!x_fgets(buf, sizeof(buf), auth))
432			continue;
433		len = strlen(buf);
434
435		if ((len) && (buf[len-1]=='\n'))
436		{
437			buf[len-1] = '\0';
438			len--;
439		}
440		if (len == 0)
441			continue;
442
443		/* break up the line into parameter & value.
444		 * will need to eat a little whitespace possibly */
445		param = buf;
446		if (!(ptr = strchr_m (buf, '=')))
447			continue;
448
449		val = ptr+1;
450		*ptr = '\0';
451
452		/* eat leading white space */
453		while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
454			val++;
455
456		if (strwicmp("password", param) == 0) {
457			set_cmdline_auth_info_password(auth_info, val);
458		} else if (strwicmp("username", param) == 0) {
459			set_cmdline_auth_info_username(auth_info, val);
460		} else if (strwicmp("domain", param) == 0) {
461			set_global_myworkgroup(val);
462		}
463		memset(buf, 0, sizeof(buf));
464	}
465	x_fclose(auth);
466}
467
468/* Handle command line options:
469 *		-U,--user
470 *		-A,--authentication-file
471 *		-k,--use-kerberos
472 *		-N,--no-pass
473 *		-S,--signing
474 *              -P --machine-pass
475 * 		-e --encrypt
476 * 		-C --use-ccache
477 */
478
479
480static void popt_common_credentials_callback(poptContext con,
481					enum poptCallbackReason reason,
482					const struct poptOption *opt,
483					const char *arg, const void *data)
484{
485	struct user_auth_info *auth_info = talloc_get_type_abort(
486		*((const char **)data), struct user_auth_info);
487	char *p;
488
489	if (reason == POPT_CALLBACK_REASON_PRE) {
490		set_cmdline_auth_info_username(auth_info, "GUEST");
491
492		if (getenv("LOGNAME")) {
493			set_cmdline_auth_info_username(auth_info,
494						       getenv("LOGNAME"));
495		}
496
497		if (getenv("USER")) {
498			char *puser = SMB_STRDUP(getenv("USER"));
499			if (!puser) {
500				exit(ENOMEM);
501			}
502			set_cmdline_auth_info_username(auth_info, puser);
503
504			if ((p = strchr_m(puser,'%'))) {
505				size_t len;
506				*p = 0;
507				len = strlen(p+1);
508				set_cmdline_auth_info_password(auth_info, p+1);
509				memset(strchr_m(getenv("USER"),'%')+1,'X',len);
510			}
511			SAFE_FREE(puser);
512		}
513
514		if (getenv("PASSWD")) {
515			set_cmdline_auth_info_password(auth_info,
516						       getenv("PASSWD"));
517		}
518
519		if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
520			get_password_file(auth_info);
521		}
522
523		return;
524	}
525
526	switch(opt->val) {
527	case 'U':
528		{
529			char *lp;
530			char *puser = SMB_STRDUP(arg);
531
532			if ((lp=strchr_m(puser,'%'))) {
533				size_t len;
534				*lp = 0;
535				set_cmdline_auth_info_username(auth_info,
536							       puser);
537				set_cmdline_auth_info_password(auth_info,
538							       lp+1);
539				len = strlen(lp+1);
540				memset(strchr_m(arg,'%')+1,'X',len);
541			} else {
542				set_cmdline_auth_info_username(auth_info,
543							       puser);
544			}
545			SAFE_FREE(puser);
546		}
547		break;
548
549	case 'A':
550		get_credentials_file(auth_info, arg);
551		break;
552
553	case 'k':
554#ifndef HAVE_KRB5
555		d_printf("No kerberos support compiled in\n");
556		exit(1);
557#else
558		set_cmdline_auth_info_use_krb5_ticket(auth_info);
559#endif
560		break;
561
562	case 'S':
563		if (!set_cmdline_auth_info_signing_state(auth_info, arg)) {
564			fprintf(stderr, "Unknown signing option %s\n", arg );
565			exit(1);
566		}
567		break;
568	case 'P':
569		set_cmdline_auth_info_use_machine_account(auth_info);
570		break;
571	case 'N':
572		set_cmdline_auth_info_password(auth_info, "");
573		break;
574	case 'e':
575		set_cmdline_auth_info_smb_encrypt(auth_info);
576		break;
577	case 'C':
578		set_cmdline_auth_info_use_ccache(auth_info, true);
579		break;
580	}
581}
582
583static struct user_auth_info *global_auth_info;
584
585void popt_common_set_auth_info(struct user_auth_info *auth_info)
586{
587	global_auth_info = auth_info;
588}
589
590struct poptOption popt_common_credentials[] = {
591	{ NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE,
592	  (void *)popt_common_credentials_callback, 0,
593	  (const char *)&global_auth_info },
594	{ "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
595	{ "no-pass", 'N', POPT_ARG_NONE, NULL, 'N', "Don't ask for a password" },
596	{ "kerberos", 'k', POPT_ARG_NONE, NULL, 'k', "Use kerberos (active directory) authentication" },
597	{ "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
598	{ "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
599	{"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" },
600	{"encrypt", 'e', POPT_ARG_NONE, NULL, 'e', "Encrypt SMB transport (UNIX extended servers only)" },
601	{"use-ccache", 'C', POPT_ARG_NONE, NULL, 'C',
602	 "Use the winbind ccache for authentication" },
603	POPT_TABLEEND
604};
605