lib: Remove global xfile.h includes
[samba.git] / source3 / lib / popt_common.c
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 #include "system/filesys.h"
25 #include "popt_common.h"
26 #include "lib/param/param.h"
27 #include "lib/util/xfile.h"
28
29 /* Handle command line options:
30  *              -d,--debuglevel 
31  *              -s,--configfile 
32  *              -O,--socket-options 
33  *              -V,--version
34  *              -l,--log-base
35  *              -n,--netbios-name
36  *              -W,--workgroup
37  *              -i,--scope
38  */
39
40 enum {OPT_OPTION=1};
41
42 extern bool override_logfile;
43
44 static void set_logfile(poptContext con, const char * arg)
45 {
46
47         char *lfile = NULL;
48         const char *pname;
49
50         /* Find out basename of current program */
51         pname = strrchr_m(poptGetInvocationName(con),'/');
52
53         if (!pname)
54                 pname = poptGetInvocationName(con);
55         else
56                 pname++;
57
58         if (asprintf(&lfile, "%s/log.%s", arg, pname) < 0) {
59                 return;
60         }
61         lp_set_logfile(lfile);
62         SAFE_FREE(lfile);
63 }
64
65 static bool PrintSambaVersionString;
66
67 static void popt_s3_talloc_log_fn(const char *message)
68 {
69         DEBUG(0,("%s", message));
70 }
71
72 static void popt_common_callback(poptContext con,
73                            enum poptCallbackReason reason,
74                            const struct poptOption *opt,
75                            const char *arg, const void *data)
76 {
77
78         if (reason == POPT_CALLBACK_REASON_PRE) {
79                 set_logfile(con, get_dyn_LOGFILEBASE());
80                 talloc_set_log_fn(popt_s3_talloc_log_fn);
81                 talloc_set_abort_fn(smb_panic);
82                 return;
83         }
84
85         if (reason == POPT_CALLBACK_REASON_POST) {
86
87                 if (PrintSambaVersionString) {
88                         printf( "Version %s\n", samba_version_string());
89                         exit(0);
90                 }
91
92                 if (is_default_dyn_CONFIGFILE()) {
93                         if(getenv("SMB_CONF_PATH")) {
94                                 set_dyn_CONFIGFILE(getenv("SMB_CONF_PATH"));
95                         }
96                 }
97
98                 /* Further 'every Samba program must do this' hooks here. */
99                 return;
100         }
101
102         switch(opt->val) {
103         case OPT_OPTION:
104         {
105                 struct loadparm_context *lp_ctx;
106
107                 lp_ctx = loadparm_init_s3(talloc_tos(), loadparm_s3_helpers());
108                 if (lp_ctx == NULL) {
109                         fprintf(stderr, "loadparm_init_s3() failed!\n");
110                         exit(1);
111                 }
112
113                 if (!lpcfg_set_option(lp_ctx, arg)) {
114                         fprintf(stderr, "Error setting option '%s'\n", arg);
115                         exit(1);
116                 }
117                 TALLOC_FREE(lp_ctx);
118                 break;
119         }
120         case 'd':
121                 if (arg) {
122                         lp_set_cmdline("log level", arg);
123                 }
124                 break;
125
126         case 'V':
127                 PrintSambaVersionString = True;
128                 break;
129
130         case 'O':
131                 if (arg) {
132                         lp_set_cmdline("socket options", arg);
133                 }
134                 break;
135
136         case 's':
137                 if (arg) {
138                         set_dyn_CONFIGFILE(arg);
139                 }
140                 break;
141
142         case 'n':
143                 if (arg) {
144                         lp_set_cmdline("netbios name", arg);
145                 }
146                 break;
147
148         case 'l':
149                 if (arg) {
150                         set_logfile(con, arg);
151                         override_logfile = True;
152                         set_dyn_LOGFILEBASE(arg);
153                 }
154                 break;
155
156         case 'i':
157                 if (arg) {
158                         lp_set_cmdline("netbios scope", arg);
159                 }
160                 break;
161
162         case 'W':
163                 if (arg) {
164                         lp_set_cmdline("workgroup", arg);
165                 }
166                 break;
167         }
168 }
169
170 struct poptOption popt_common_connection[] = {
171         { NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
172         { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use",
173           "SOCKETOPTIONS" },
174         { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" },
175         { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" },
176         { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" },
177
178         POPT_TABLEEND
179 };
180
181 struct poptOption popt_common_samba[] = {
182         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, (void *)popt_common_callback },
183         { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
184         { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternate configuration file", "CONFIGFILE" },
185         { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Base name for log files", "LOGFILEBASE" },
186         { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
187         { "option",         0, POPT_ARG_STRING, NULL, OPT_OPTION, "Set smb.conf option from command line", "name=value" },
188         POPT_TABLEEND
189 };
190
191 struct poptOption popt_common_configfile[] = {
192         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, (void *)popt_common_callback },
193         { "configfile", 0, POPT_ARG_STRING, NULL, 's', "Use alternate configuration file", "CONFIGFILE" },
194         POPT_TABLEEND
195 };
196
197 struct poptOption popt_common_version[] = {
198         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_POST, (void *)popt_common_callback },
199         { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" },
200         POPT_TABLEEND
201 };
202
203 struct poptOption popt_common_debuglevel[] = {
204         { NULL, 0, POPT_ARG_CALLBACK, (void *)popt_common_callback },
205         { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" },
206         POPT_TABLEEND
207 };
208
209 struct poptOption popt_common_option[] = {
210         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_POST, (void *)popt_common_callback },
211         { "option",         0, POPT_ARG_STRING, NULL, OPT_OPTION, "Set smb.conf option from command line", "name=value" },
212         POPT_TABLEEND
213 };
214
215 /****************************************************************************
216  * get a password from a a file or file descriptor
217  * exit on failure
218  * ****************************************************************************/
219
220 static void get_password_file(struct user_auth_info *auth_info)
221 {
222         int fd = -1;
223         char *p;
224         bool close_it = False;
225         char *spec = NULL;
226         char pass[128];
227
228         if ((p = getenv("PASSWD_FD")) != NULL) {
229                 if (asprintf(&spec, "descriptor %s", p) < 0) {
230                         return;
231                 }
232                 sscanf(p, "%d", &fd);
233                 close_it = false;
234         } else if ((p = getenv("PASSWD_FILE")) != NULL) {
235                 fd = open(p, O_RDONLY, 0);
236                 spec = SMB_STRDUP(p);
237                 if (fd < 0) {
238                         fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n",
239                                         spec, strerror(errno));
240                         exit(1);
241                 }
242                 close_it = True;
243         }
244
245         if (fd < 0) {
246                 fprintf(stderr, "fd = %d, < 0\n", fd);
247                 exit(1);
248         }
249
250         for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */
251                 p && p - pass < sizeof(pass);) {
252                 switch (read(fd, p, 1)) {
253                 case 1:
254                         if (*p != '\n' && *p != '\0') {
255                                 *++p = '\0'; /* advance p, and null-terminate pass */
256                                 break;
257                         }
258                 case 0:
259                         if (p - pass) {
260                                 *p = '\0'; /* null-terminate it, just in case... */
261                                 p = NULL; /* then force the loop condition to become false */
262                                 break;
263                         } else {
264                                 fprintf(stderr, "Error reading password from file %s: %s\n",
265                                                 spec, "empty password\n");
266                                 SAFE_FREE(spec);
267                                 exit(1);
268                         }
269
270                 default:
271                         fprintf(stderr, "Error reading password from file %s: %s\n",
272                                         spec, strerror(errno));
273                         SAFE_FREE(spec);
274                         exit(1);
275                 }
276         }
277         SAFE_FREE(spec);
278
279         set_cmdline_auth_info_password(auth_info, pass);
280         if (close_it) {
281                 close(fd);
282         }
283 }
284
285 static void get_credentials_file(struct user_auth_info *auth_info,
286                                  const char *file)
287 {
288         XFILE *auth;
289         fstring buf;
290         uint16_t len = 0;
291         char *ptr, *val, *param;
292
293         if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL)
294         {
295                 /* fail if we can't open the credentials file */
296                 d_printf("ERROR: Unable to open credentials file!\n");
297                 exit(-1);
298         }
299
300         while (!x_feof(auth))
301         {
302                 /* get a line from the file */
303                 if (!x_fgets(buf, sizeof(buf), auth))
304                         continue;
305                 len = strlen(buf);
306
307                 if ((len) && (buf[len-1]=='\n'))
308                 {
309                         buf[len-1] = '\0';
310                         len--;
311                 }
312                 if (len == 0)
313                         continue;
314
315                 /* break up the line into parameter & value.
316                  * will need to eat a little whitespace possibly */
317                 param = buf;
318                 if (!(ptr = strchr_m (buf, '=')))
319                         continue;
320
321                 val = ptr+1;
322                 *ptr = '\0';
323
324                 /* eat leading white space */
325                 while ((*val!='\0') && ((*val==' ') || (*val=='\t')))
326                         val++;
327
328                 if (strwicmp("password", param) == 0) {
329                         set_cmdline_auth_info_password(auth_info, val);
330                 } else if (strwicmp("username", param) == 0) {
331                         set_cmdline_auth_info_username(auth_info, val);
332                 } else if (strwicmp("domain", param) == 0) {
333                         set_cmdline_auth_info_domain(auth_info, val);
334                 }
335                 memset(buf, 0, sizeof(buf));
336         }
337         x_fclose(auth);
338 }
339
340 /* Handle command line options:
341  *              -U,--user
342  *              -A,--authentication-file
343  *              -k,--use-kerberos
344  *              -N,--no-pass
345  *              -S,--signing
346  *              -P --machine-pass
347  *              -e --encrypt
348  *              -C --use-ccache
349  */
350
351 struct user_auth_info *cmdline_auth_info;
352 static bool popt_common_credentials_delay_post;
353
354 void popt_common_credentials_set_delay_post(void)
355 {
356         popt_common_credentials_delay_post = true;
357 }
358
359 void popt_common_credentials_post(void)
360 {
361         struct user_auth_info *auth_info = cmdline_auth_info;
362
363         if (get_cmdline_auth_info_use_machine_account(auth_info) &&
364             !set_cmdline_auth_info_machine_account_creds(auth_info))
365         {
366                 fprintf(stderr,
367                         "Failed to use machine account credentials\n");
368                 exit(1);
369         }
370
371         set_cmdline_auth_info_getpass(auth_info);
372 }
373
374 static void popt_common_credentials_callback(poptContext con,
375                                         enum poptCallbackReason reason,
376                                         const struct poptOption *opt,
377                                         const char *arg, const void *data)
378 {
379         struct user_auth_info *auth_info = cmdline_auth_info;
380
381         if (reason == POPT_CALLBACK_REASON_PRE) {
382                 if (auth_info == NULL) {
383                         auth_info = user_auth_info_init(talloc_autofree_context());
384                         if (auth_info == NULL) {
385                                 fprintf(stderr, "user_auth_info_init() failed\n");
386                                 exit(1);
387                         }
388                         cmdline_auth_info = auth_info;
389                 }
390
391                 set_cmdline_auth_info_username(auth_info, "GUEST");
392
393                 if (getenv("LOGNAME")) {
394                         set_cmdline_auth_info_username(auth_info,
395                                                        getenv("LOGNAME"));
396                 }
397
398                 if (getenv("USER")) {
399                         set_cmdline_auth_info_username(auth_info,
400                                                        getenv("USER"));
401                 }
402
403                 if (getenv("PASSWD")) {
404                         set_cmdline_auth_info_password(auth_info,
405                                                        getenv("PASSWD"));
406                 }
407
408                 if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) {
409                         get_password_file(auth_info);
410                 }
411
412                 return;
413         }
414
415         if (reason == POPT_CALLBACK_REASON_POST) {
416                 if (popt_common_credentials_delay_post) {
417                         return;
418                 }
419
420                 popt_common_credentials_post();
421                 return;
422         }
423
424         switch(opt->val) {
425         case 'U':
426                 {
427                         char *lp;
428                         char *puser = SMB_STRDUP(arg);
429
430                         if ((lp=strchr_m(puser,'%'))) {
431                                 size_t len;
432                                 *lp = '\0';
433                                 set_cmdline_auth_info_username(auth_info,
434                                                                puser);
435                                 set_cmdline_auth_info_password(auth_info,
436                                                                lp+1);
437                                 len = strlen(lp+1);
438                                 memset(lp + 1, '\0', len);
439                         } else {
440                                 set_cmdline_auth_info_username(auth_info,
441                                                                puser);
442                         }
443                         SAFE_FREE(puser);
444                 }
445                 break;
446
447         case 'A':
448                 get_credentials_file(auth_info, arg);
449                 break;
450
451         case 'k':
452 #ifndef HAVE_KRB5
453                 d_printf("No kerberos support compiled in\n");
454                 exit(1);
455 #else
456                 set_cmdline_auth_info_use_krb5_ticket(auth_info);
457 #endif
458                 break;
459
460         case 'S':
461                 if (!set_cmdline_auth_info_signing_state(auth_info, arg)) {
462                         fprintf(stderr, "Unknown signing option %s\n", arg );
463                         exit(1);
464                 }
465                 break;
466         case 'P':
467                 set_cmdline_auth_info_use_machine_account(auth_info);
468                 break;
469         case 'N':
470                 set_cmdline_auth_info_password(auth_info, "");
471                 break;
472         case 'e':
473                 set_cmdline_auth_info_smb_encrypt(auth_info);
474                 break;
475         case 'C':
476                 set_cmdline_auth_info_use_ccache(auth_info, true);
477                 break;
478         case 'H':
479                 set_cmdline_auth_info_use_pw_nt_hash(auth_info, true);
480                 break;
481         }
482 }
483
484 /**
485  * @brief Burn the commandline password.
486  *
487  * This function removes the password from the command line so we
488  * don't leak the password e.g. in 'ps aux'.
489  *
490  * It should be called after processing the options and you should pass down
491  * argv from main().
492  *
493  * @param[in]  argc     The number of arguments.
494  *
495  * @param[in]  argv[]   The argument array we will find the array.
496  */
497 void popt_burn_cmdline_password(int argc, char *argv[])
498 {
499         bool found = false;
500         char *p = NULL;
501         int i, ulen = 0;
502
503         for (i = 0; i < argc; i++) {
504                 p = argv[i];
505                 if (strncmp(p, "-U", 2) == 0) {
506                         ulen = 2;
507                         found = true;
508                 } else if (strncmp(p, "--user", 6) == 0) {
509                         ulen = 6;
510                         found = true;
511                 }
512
513                 if (found) {
514                         if (p == NULL) {
515                                 return;
516                         }
517
518                         if (strlen(p) == ulen) {
519                                 continue;
520                         }
521
522                         p = strchr_m(p, '%');
523                         if (p != NULL) {
524                                 memset(p, '\0', strlen(p));
525                         }
526                         found = false;
527                 }
528         }
529 }
530
531 struct poptOption popt_common_credentials[] = {
532         { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST,
533           (void *)popt_common_credentials_callback, 0, NULL },
534         { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" },
535         { "no-pass", 'N', POPT_ARG_NONE, NULL, 'N', "Don't ask for a password" },
536         { "kerberos", 'k', POPT_ARG_NONE, NULL, 'k', "Use kerberos (active directory) authentication" },
537         { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" },
538         { "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" },
539         {"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" },
540         {"encrypt", 'e', POPT_ARG_NONE, NULL, 'e', "Encrypt SMB transport" },
541         {"use-ccache", 'C', POPT_ARG_NONE, NULL, 'C',
542          "Use the winbind ccache for authentication" },
543         {"pw-nt-hash", '\0', POPT_ARG_NONE, NULL, 'H',
544          "The supplied password is the NT hash" },
545         POPT_TABLEEND
546 };