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 2 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, write to the Free Software
21   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22*/
23
24#include "includes.h"
25
26/* Handle command line options:
27 *		-d,--debuglevel
28 *		-s,--configfile
29 *		-O,--socket-options
30 *		-V,--version
31 *		-l,--log-base
32 *		-n,--netbios-name
33 *		-W,--workgroup
34 *		-i,--scope
35 */
36
37extern pstring user_socket_options;
38extern BOOL AllowDebugChange;
39extern BOOL override_logfile;
40
41struct user_auth_info cmdline_auth_info;
42
43static void set_logfile(poptContext con, const char * arg)
44{
45
46	pstring logfile;
47	const char *pname;
48
49	/* Find out basename of current program */
50	pname = strrchr_m(poptGetInvocationName(con),'/');
51
52	if (!pname)
53		pname = poptGetInvocationName(con);
54	else
55		pname++;
56
57	pstr_sprintf(logfile, "%s/log.%s", arg, pname);
58	lp_set_logfile(logfile);
59}
60
61static void popt_common_callback(poptContext con,
62			   enum poptCallbackReason reason,
63			   const struct poptOption *opt,
64			   const char *arg, const void *data)
65{
66
67	if (reason == POPT_CALLBACK_REASON_PRE) {
68		set_logfile(con, dyn_LOGFILEBASE);
69		return;
70	}
71
72	switch(opt->val) {
73	case 'd':
74		if (arg) {
75			debug_parse_levels(arg);
76			AllowDebugChange = False;
77		}
78		break;
79
80	case 'V':
81		printf( "Version %s\n", SAMBA_VERSION_STRING);
82		exit(0);
83		break;
84
85	case 'O':
86		if (arg) {
87			pstrcpy(user_socket_options,arg);
88		}
89		break;
90
91	case 's':
92		if (arg) {
93			pstrcpy(dyn_CONFIGFILE, arg);
94		}
95		break;
96
97	case 'n':
98		if (arg) {
99			set_global_myname(arg);
100		}
101		break;
102
103	case 'l':
104		if (arg) {
105			set_logfile(con, arg);
106			override_logfile = True;
107			pstr_sprintf(dyn_LOGFILEBASE, "%s", arg);
108		}
109		break;
110
111	case 'i':
112		if (arg) {
113			  set_global_scope(arg);
114		}
115		break;
116
117	case 'W':
118		if (arg) {
119			set_global_myworkgroup(arg);
120		}
121		break;
122	}
123}
124
125struct poptOption popt_common_connection[] = {
126	{ NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
127	{ "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
128	  "SOCKETOPTIONS" },
129	{ "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
130	{ "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
131	{ "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
132
133	POPT_TABLEEND
134};
135
136struct poptOption popt_common_samba[] = {
137	{ NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, (void *)popt_common_callback },
138	{ "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
139	{ "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternate configuration file", "CONFIGFILE" },
140	{ "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Base name for log files", "LOGFILEBASE" },
141	{ "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
142	POPT_TABLEEND
143};
144
145struct poptOption popt_common_version[] = {
146	{ NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
147	{ "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
148	POPT_TABLEEND
149};
150
151
152/* Handle command line options:
153 *		--sbindir
154 *		--bindir
155 *		--swatdir
156 *		--lmhostsfile
157 *		--libdir
158 *		--shlibext
159 *		--lockdir
160 *		--piddir
161 *		--smb-passwd-file
162 *		--private-dir
163 */
164
165enum dyn_item{
166	DYN_SBINDIR = 1,
167	DYN_BINDIR,
168	DYN_SWATDIR,
169	DYN_LMHOSTSFILE,
170	DYN_LIBDIR,
171	DYN_SHLIBEXT,
172	DYN_LOCKDIR,
173	DYN_PIDDIR,
174	DYN_SMB_PASSWD_FILE,
175	DYN_PRIVATE_DIR,
176};
177
178
179static void popt_dynconfig_callback(poptContext con,
180			   enum poptCallbackReason reason,
181			   const struct poptOption *opt,
182			   const char *arg, const void *data)
183{
184
185	switch (opt->val) {
186	case DYN_SBINDIR:
187		if (arg) {
188			dyn_SBINDIR = SMB_STRDUP(arg);
189		}
190		break;
191
192	case DYN_BINDIR:
193		if (arg) {
194			dyn_BINDIR = SMB_STRDUP(arg);
195		}
196		break;
197
198	case DYN_SWATDIR:
199		if (arg) {
200			dyn_SWATDIR = SMB_STRDUP(arg);
201		}
202		break;
203
204	case DYN_LMHOSTSFILE:
205		if (arg) {
206			pstrcpy(dyn_LMHOSTSFILE, arg);
207		}
208		break;
209
210	case DYN_LIBDIR:
211		if (arg) {
212			pstrcpy(dyn_LIBDIR, arg);
213		}
214		break;
215
216	case DYN_SHLIBEXT:
217		if (arg) {
218			fstrcpy(dyn_SHLIBEXT, arg);
219		}
220		break;
221
222	case DYN_LOCKDIR:
223		if (arg) {
224			pstrcpy(dyn_LOCKDIR, arg);
225		}
226		break;
227
228	case DYN_PIDDIR:
229		if (arg) {
230			pstrcpy(dyn_PIDDIR, arg);
231		}
232		break;
233
234	case DYN_SMB_PASSWD_FILE:
235		if (arg) {
236			pstrcpy(dyn_SMB_PASSWD_FILE, arg);
237		}
238		break;
239
240	case DYN_PRIVATE_DIR:
241		if (arg) {
242			pstrcpy(dyn_PRIVATE_DIR, arg);
243		}
244		break;
245
246	}
247}
248
249const struct poptOption popt_common_dynconfig[] = {
250
251	{ NULL, '\0', POPT_ARG_CALLBACK, (void *)popt_dynconfig_callback },
252
253	{ "sbindir", '\0' , POPT_ARG_STRING, NULL, DYN_SBINDIR,
254	    "Path to sbin directory", "SBINDIR" },
255	{ "bindir", '\0' , POPT_ARG_STRING, NULL, DYN_BINDIR,
256	    "Path to bin directory", "BINDIR" },
257	{ "swatdir", '\0' , POPT_ARG_STRING, NULL, DYN_SWATDIR,
258	    "Path to SWAT installation directory", "SWATDIR" },
259	{ "lmhostsfile", '\0' , POPT_ARG_STRING, NULL, DYN_LMHOSTSFILE,
260	    "Path to lmhosts file", "LMHOSTSFILE" },
261	{ "libdir", '\0' , POPT_ARG_STRING, NULL, DYN_LIBDIR,
262	    "Path to shared library directory", "LIBDIR" },
263	{ "shlibext", '\0' , POPT_ARG_STRING, NULL, DYN_SHLIBEXT,
264	    "Shared library extension", "SHLIBEXT" },
265	{ "lockdir", '\0' , POPT_ARG_STRING, NULL, DYN_LOCKDIR,
266	    "Path to lock file directory", "LOCKDIR" },
267	{ "piddir", '\0' , POPT_ARG_STRING, NULL, DYN_PIDDIR,
268	    "Path to PID file directory", "PIDDIR" },
269	{ "smb-passwd-file", '\0' , POPT_ARG_STRING, NULL, DYN_SMB_PASSWD_FILE,
270	    "Path to smbpasswd file", "SMB_PASSWD_FILE" },
271	{ "private-dir", '\0' , POPT_ARG_STRING, NULL, DYN_PRIVATE_DIR,
272	    "Path to private data directory", "PRIVATE_DIR" },
273
274	POPT_TABLEEND
275};
276
277/****************************************************************************
278 * get a password from a a file or file descriptor
279 * exit on failure
280 * ****************************************************************************/
281static void get_password_file(struct user_auth_info *a)
282{
283	int fd = -1;
284	char *p;
285	BOOL close_it = False;
286	pstring spec;
287	char pass[128];
288
289	if ((p = getenv("PASSWD_FD")) != NULL) {
290		pstrcpy(spec, "descriptor ");
291		pstrcat(spec, p);
292		sscanf(p, "%d", &fd);
293		close_it = False;
294	} else if ((p = getenv("PASSWD_FILE")) != NULL) {
295		fd = sys_open(p, O_RDONLY, 0);
296		pstrcpy(spec, p);
297		if (fd < 0) {
298			fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
299					spec, strerror(errno));
300			exit(1);
301		}
302		close_it = True;
303	}
304
305	for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
306		p && p - pass < sizeof(pass);) {
307		switch (read(fd, p, 1)) {
308		case 1:
309			if (*p != '\n' && *p != '\0') {
310				*++p = '\0'; /* advance p, and null-terminate pass */
311				break;
312			}
313		case 0:
314			if (p - pass) {
315				*p = '\0'; /* null-terminate it, just in case... */
316				p = NULL; /* then force the loop condition to become false */
317				break;
318			} else {
319				fprintf(stderr, "Error reading password from file %s: %s\n",
320						spec, "empty password\n");
321				exit(1);
322			}
323
324		default:
325			fprintf(stderr, "Error reading password from file %s: %s\n",
326					spec, strerror(errno));
327			exit(1);
328		}
329	}
330	pstrcpy(a->password, pass);
331	if (close_it)
332		close(fd);
333}
334
335static void get_credentials_file(const char *file, struct user_auth_info *info)
336{
337	XFILE *auth;
338	fstring buf;
339	uint16 len = 0;
340	char *ptr, *val, *param;
341
342	if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
343	{
344		/* fail if we can't open the credentials file */
345		d_printf("ERROR: Unable to open credentials file!\n");
346		exit(-1);
347	}
348
349	while (!x_feof(auth))
350	{
351		/* get a line from the file */
352		if (!x_fgets(buf, sizeof(buf), auth))
353			continue;
354		len = strlen(buf);
355
356		if ((len) && (buf[len-1]=='\n'))
357		{
358			buf[len-1] = '\0';
359			len--;
360		}
361		if (len == 0)
362			continue;
363
364		/* break up the line into parameter & value.
365		 * will need to eat a little whitespace possibly */
366		param = buf;
367		if (!(ptr = strchr_m (buf, '=')))
368			continue;
369
370		val = ptr+1;
371		*ptr = '\0';
372
373		/* eat leading white space */
374		while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
375			val++;
376
377		if (strwicmp("password", param) == 0)
378		{
379			pstrcpy(info->password, val);
380			info->got_pass = True;
381		}
382		else if (strwicmp("username", param) == 0)
383			pstrcpy(info->username, val);
384		else if (strwicmp("domain", param) == 0)
385			set_global_myworkgroup(val);
386		memset(buf, 0, sizeof(buf));
387	}
388	x_fclose(auth);
389}
390
391/* Handle command line options:
392 *		-U,--user
393 *		-A,--authentication-file
394 *		-k,--use-kerberos
395 *		-N,--no-pass
396 *		-S,--signing
397 *              -P --machine-pass
398 */
399
400
401static void popt_common_credentials_callback(poptContext con,
402					enum poptCallbackReason reason,
403					const struct poptOption *opt,
404					const char *arg, const void *data)
405{
406	char *p;
407
408	if (reason == POPT_CALLBACK_REASON_PRE) {
409		cmdline_auth_info.use_kerberos = False;
410		cmdline_auth_info.got_pass = False;
411		cmdline_auth_info.signing_state = Undefined;
412		pstrcpy(cmdline_auth_info.username, "GUEST");
413
414		if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME"));
415
416		if (getenv("USER")) {
417			pstrcpy(cmdline_auth_info.username,getenv("USER"));
418
419			if ((p = strchr_m(cmdline_auth_info.username,'%'))) {
420				*p = 0;
421				pstrcpy(cmdline_auth_info.password,p+1);
422				cmdline_auth_info.got_pass = True;
423				memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password));
424			}
425		}
426
427		if (getenv("PASSWD")) {
428			pstrcpy(cmdline_auth_info.password,getenv("PASSWD"));
429			cmdline_auth_info.got_pass = True;
430		}
431
432		if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
433			get_password_file(&cmdline_auth_info);
434			cmdline_auth_info.got_pass = True;
435		}
436
437		return;
438	}
439
440	switch(opt->val) {
441	case 'U':
442		{
443			char *lp;
444
445			pstrcpy(cmdline_auth_info.username,arg);
446			if ((lp=strchr_m(cmdline_auth_info.username,'%'))) {
447				*lp = 0;
448				pstrcpy(cmdline_auth_info.password,lp+1);
449				cmdline_auth_info.got_pass = True;
450				memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password));
451			}
452		}
453		break;
454
455	case 'A':
456		get_credentials_file(arg, &cmdline_auth_info);
457		break;
458
459	case 'k':
460#ifndef HAVE_KRB5
461		d_printf("No kerberos support compiled in\n");
462		exit(1);
463#else
464		cmdline_auth_info.use_kerberos = True;
465		cmdline_auth_info.got_pass = True;
466#endif
467		break;
468
469	case 'S':
470		{
471			cmdline_auth_info.signing_state = -1;
472			if (strequal(arg, "off") || strequal(arg, "no") || strequal(arg, "false"))
473				cmdline_auth_info.signing_state = False;
474			else if (strequal(arg, "on") || strequal(arg, "yes") || strequal(arg, "true") ||
475					strequal(arg, "auto") )
476				cmdline_auth_info.signing_state = True;
477			else if (strequal(arg, "force") || strequal(arg, "required") || strequal(arg, "forced"))
478				cmdline_auth_info.signing_state = Required;
479			else {
480				fprintf(stderr, "Unknown signing option %s\n", arg );
481				exit(1);
482			}
483		}
484		break;
485	case 'P':
486	        {
487			char *opt_password = NULL;
488			/* it is very useful to be able to make ads queries as the
489			   machine account for testing purposes and for domain leave */
490
491			if (!secrets_init()) {
492				d_printf("ERROR: Unable to open secrets database\n");
493				exit(1);
494			}
495
496			opt_password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
497
498			if (!opt_password) {
499				d_printf("ERROR: Unable to fetch machine password\n");
500				exit(1);
501			}
502			pstr_sprintf(cmdline_auth_info.username, "%s$",
503				     global_myname());
504			pstrcpy(cmdline_auth_info.password,opt_password);
505			SAFE_FREE(opt_password);
506
507			/* machine accounts only work with kerberos */
508			cmdline_auth_info.use_kerberos = True;
509			cmdline_auth_info.got_pass = True;
510		}
511		break;
512	}
513}
514
515
516
517struct poptOption popt_common_credentials[] = {
518	{ NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, (void *)popt_common_credentials_callback },
519	{ "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
520	{ "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, 0, "Don't ask for a password" },
521	{ "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, 'k', "Use kerberos (active directory) authentication" },
522	{ "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
523	{ "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
524	{"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" },
525	POPT_TABLEEND
526};
527